How to wait till animation finish in pure javascript - javascript

Im trying to make an infinite loop for a carousel when i click a desired div it will be centered at a desired position, if i click the item that is adjacent to the centered position, everything works fine, but if he is more than 1 position away from the center it triggers an effect that doesnt follow the usual logic.
I have tried to solve the problem by checking the distance from the center and than moving the items 1 by 1 n times, but i guess because the loop doesn't wait for the animations to finish im getting this weird effect.
The final outcome should be making an infinite feel to the carousel when you click an item that is 5 positions away from the center it would center it and the ones that are out of view will slide from the respective direction to create a loop
any help will be appreciated, Im relatively new to web dev so a well explained answer will be highly appreciated
JS:
const serviceList = document.querySelectorAll('.service__block');
serviceList.forEach(service => {
service.addEventListener('click', () => {
markSelectedService(service);
moveService(checkDistance(service));
});
});
//Adds the class to the clicked service
function markSelectedService(service) {
removeSelectedClass();
service.classList.add('selected');
}
//Removes the selected class from all the services
function removeSelectedClass() {
serviceList.forEach(service => {
service.classList.remove('selected');
});
}
//Check distance from center
function checkDistance(service) {
let distance = service.dataset.order - 4;
return distance;
}
//Move the service 1 by 1 n times
function moveService(distance) {
if (distance > 0) {
for (var i = 0; i < distance; i++) {
serviceList.forEach(service => {
service.dataset.order = parseInt(service.dataset.order) - 1;
if (parseInt(service.dataset.order) === -1) {
service.dataset.order = 11;
}
});
}
} else if (distance < 0) {
distance = distance * -1;
for (var i = 0; i < distance; i++) {
serviceList.forEach(service => {
service.dataset.order = parseInt(service.dataset.order) + 1;
if (parseInt(service.dataset.order) === 12) {
service.dataset.order = 0;
}
});
}
}
}
Link to codepen:
https://codepen.io/tomyshoam/pen/yLLLYyQ

If you want to wait for the animation's end to trigger the next one you insert a setInterval and set the interval time to your animation's time.
Example:
var i = 0;
var animationInterval = setInterval(function () {
//YOUR LOGIC HERE
if (++i === distance) {
window.clearInterval(animationInterval);
}
}, 500);
Update
After some tries I think I got it working how you wanted, although it's not the better way to do it.
I copied the icons list, one to the right side and one to the left, this way the icons will only move to the other side when they are far far away from the user's view and will not trigger that weir behavior.
Code below:
https://codepen.io/diogoperes/pen/oNNNBGY

Related

How to get randomly generated array to reset in simple memory game with jQuery

I'm sorry if this has been asked before,
I've searched through Stackoverflow but couldn't find anything that answered my problem.
I'm building a simple memory game, an online version of Simon, when you click the "Start" button it runs the code below to create a random array (of length 4) out of the four colour buttons.
But when you click "Start" again for the next round it doesn't clear the array, and instead creates a second one, and then checks your input against both telling you you're both right and wrong, or right and right (depending on the random array created out of the buttons).
I've tried buttonsToClick = [] in the else section, but it doesn't reset.
I don't know what I'm missing, I've only been learning JavaScript/jQuery for about a month but I wanted to test my knowledge.
The code snipped:
var score = 0;
$("#score").html(`${score}`);
$("#button5").on("click", function() {
var buttons = document.getElementsByClassName("js-button");
var buttonsToClick = chooseRandomButtons(buttons);
currentButtons = buttonsToClick;
flashButtons(buttonsToClick, 0);
var currentOrder = 0;
$(".js-button").on("click", function() {
var selectedButton = $(this)[0];
var button = currentButtons[0];
if (selectedButton === button) {
currentButtons.splice(button,1);
/*alert("Correct");*/
score += 1;
$("#score").html(`${score}`);
} else {
currentButtons = buttonsToClick;
alert("Wrong. Click 'Start' to try again");
score = 0;
$("#score").html(`${score}`);
}
});
})
function chooseRandomButtons(buttons) {
var buttonsToClick = [];
var maxRandomNumber = buttons.length - 1;
for (var i = 0; i < 4; i++) {
buttonsToClick.push(buttons[randomIntFromInterval(0, maxRandomNumber)]);
}
return buttonsToClick;
}
function randomIntFromInterval(min, max) { // min and max included
return Math.floor(Math.random() * (max - min + 1) + min);
}
function flashButtons(buttonsToClick, index) {
setTimeout(function() {
$(buttonsToClick[index]).fadeOut(500).fadeIn(500);
if (index === buttonsToClick.length - 1) {
return;
}
flashButtons(buttonsToClick, index = index + 1);
}, 1000);
}
welcome to SO.
In general you're doing anything correct with your arrays.
The issue is your event handler.
Every time you click the #button5, which I guess is the start button, you register on all your .js-button a new listener. Since you're not unbinding the old event listeners they're still active.
Since the old event listeners have a reference to your old array, you're basically checking the button against the old game and the new game.
Your solution would be to unregister the old one before registering the new one.
This could be done for example by the .off method.
Your code should then look like this:
var currentOrder = 0;
$(".js-button").off("click").on("click", function() {
var selectedButton = $(this)[0];
var button = currentButtons[0];
if (selectedButton === button) {
currentButtons.splice(button,1);
/*alert("Correct");*/
score += 1;
$("#score").html(`${score}`);
} else {
currentButtons = buttonsToClick;
alert("Wrong. Click 'Start' to try again");
score = 0;
$("#score").html(`${score}`);
}
});
Notice the .off() there.
The documentation about the method could be found here: https://api.jquery.com/off/

Dynamically moving element position using Javascript

Heres my codepen: https://codepen.io/apasric4/pen/qBWyqqb
This is the code that's causing me issues:
const moveBlock=()=> {
let x=myBlock.offsetLeft;
let y=myBlock.offsetTop;
keyArray.forEach(key=> {
if (key==='ArrowRight') {
myBlock.style.left=`${x+50}px`
} else if (key==='ArrowLeft') {
myBlock.style.left=`${x-50}px`
} else if (key==='ArrowDown') {
myBlock.style.top=`${y+50}px`
} else if (key==='ArrowUp') {
myBlock.style.top=`${y-50}px`
}
})
}
I'll briefly explain my program:
The program will start you pressing left, right, up, or down--those commands appear as a list at the top of the document. Then you'll press enter
When enter is pressed, the program goes through the array of keyboard directions and the block moves in the specified direction depending on the command in the array. There's a condition for each arrow direction to move the block 50px which way. This is a sequence so we're supposed to see happening.
However, the block only move 50px right or left, etc only once. Why is that? Am I not iterating my x and y variables correctly?
You have to calculate the value of x and y again after moving once.
Because when you are moving the block from second time, you are just using original value of x and y computed before the for loop began.
Add this in your forEach() method.
x = myBlock.offsetLeft;
y = myBlock.offsetTop;
You have later mentioned in the comments about having a delay between each move.
You can do that by creating another animate block function, having a timeout within a loop and and calling that animate block function in the timeout.
const animateBlock = (key)=> {
let x = myBlock.offsetLeft;
let y = myBlock.offsetTop;
if (key === 'ArrowRight') {
myBlock.style.left = `${x + 50}px`
} else if (key === 'ArrowLeft') {
myBlock.style.left = `${x - 50}px`
} else if (key === 'ArrowDown') {
myBlock.style.top = `${y + 50}px`
} else if (key === 'ArrowUp') {
myBlock.style.top = `${y - 50}px`
}
}
const moveBlock = () => {
keyArray.forEach((key, i) => {
setTimeout(() => {
animateBlock(key);
}, i * 1000);
})
}
Working demo: https://codepen.io/nikhilmopidevi/pen/gOYjgYd

How do I change my code to start Blinking at the same time, and still be infinite?

How do I change my code to start Blinking at the same time, and still be infinite?
function blink() {
if (!toggleSwitch) {
for (let i = 0; i < elements.length; i++) {
shapes.push(elements[i].className);
}
// Start off at the first element.
let i = 0;
let len = shapes.length;
// Do the next link
function doNext() {
let element = shapes[i];
i++;
eval(element).fillColor = eval(element).setColor;
document.getElementById(element).style.backgroundColor = (eval(element).fillColor === document.getElementById(element).style.backgroundColor) ? 'white' : eval(element).setColor;
if (i < len) {
// Don't do anything special
} else {
// Reset the counter
i = 0;
}
, myVar = setTimeout(doNext, 1000);
}
// And the code needs kicked off somewhere
doNext();
}
}
Also I can't toggle Classes because with a drag and drop I can change the background color any time during a running time so I have an object for each shape where I keep what color is the background. –
To have your page call your blink function after a certain pause first how about doing something like this?
<BODY onLoad="setTimeout('blink()', 5000);">

Javascript transitions not executing in order

I have a circle, consisting of 12 arc segments and I want to allow the user to see the transition from the start pattern to the end pattern. (there will be many start and end patterns).
Here is my code so far:
http://codepen.io/blazerix/pen/jrwNAG
function playAnimations(){
var totalLength = document.getElementsByClassName("container")[0].children.length
for(var i = 0; i < totalLength; i++){
var current_pattern = document.getElementsByClassName("container")[0].children[i]
console.log(current_pattern)
for(var j = 0; j < 12; j++){
$('#LED' + (j+1) ).css('transition-duration', '0s');
$('#LED' + (j+1) ).css({fill: current_pattern.children[1].children[j].style.backgroundColor});
}
setTimeout(function () {
for(var k = 0; k < 12; k++){
$('#LED' + (k+1) ).css('transition-duration', "" + current_pattern.children[3].children[0].value + "ms");
$('#LED' + (k+1) ).css({fill: current_pattern.children[2].children[k].style.backgroundColor});
}
}, 150);
}
}
The outer for loop goes through all of the patterns, and the two inner for loops will go through the start and end pattern respectively. For some reason, my program only displays the animation of the very last pattern. I suspect this is because the code is executing really quickly - however I am unsure of how to fix this.
Does anyone know a good workaround or what I could possibly do to rectify this issue? Any feedback or help is appreciated.
Ok, not entirely understanding all the parts of your code, I've whipped this up. It doesn't work just yet, but you may get the idea of what I'm trying to do: wait 250 milliseconds before you fire off the next animation, once you run out of siblings, bounce to the other animation. I can't spend any more time on this, but I hope this gets you where you want to be:
function playAnimations() {
var $patternHolder = $(".container");
playAnimation($('#LED1'), 0, $patternHolder, 1, 1);
}
function playAnimation($target, index, $patternHolder, childPatternIndex, animationNumber) {
//just set both fill color and transition in the object you pass in:
//.eq() chooses a child, returns jQuery object of that child by index
//Use jQuery to get background-color style
$target.css({ fill: $patternHolder.children(childPatternIndex).children().eq(index).css("background-color"), transition: "0s" });
setTimeout(function () {
if ($target.parent().next().length > 0) {
playAnimation($target.parent().next(), index++);
} else if (animationNumber == 1) {
playAnimation($("#LED1"), 0, patternHolder, 3, 2);
}
}, 250);
}

setTimeout to access DOM in for loop

Please look at this codepen: http://codepen.io/dragonOne/pen/rrwrQw (doesn't work yet!)
I want this Javascript memory board to display cards one by one in spiral order.
The spiralOrder(output) function takes in a matrix of div id's and changes each card div (id is tile_i) to display = "block" one by one in spiral order, every two seconds. But my setTimeout isn't working properly and is only displaying the first four cards (all at once...)
When I read the console.log in spiralOrder I see that the function correctly reads each card in the order that I want. But how come my setTimeout isn't working to add delay after every card display?
function spiralOrder(output) {
// Initialize our four indexes
var top = 0;
var down = output.length - 1;
var left = 0;
var right = output[0].length - 1;
var regexp = /\d+/g;
while(true)
{
// Starting showing top row
for(var j = left; j <= right; ++j) {
console.log("tile_"+output[top][j].match(regexp)); //THIS SHOWS SPIRAL ALGORITHM IS CORRECT
setTimeout(function() { document.getElementById("tile_"+output[top][j].match(regexp)).style.display = "block"; }, 2000); //THIS DOESN'T BEHAVE RIGHT
}
top++;
if(top > down || left > right) {
break;
}
//Starting showing rightmost column
for(var i = top; i <= down; ++i){
console.log("tile_"+output[i][right].match(regexp));
setTimeout(function() { document.getElementById("tile_"+output[i][right].match(regexp)).style.display = "block"; }, 2000);
}
right--;
if(top > down || left > right) {
break;
}
//Starting showing bottom row
for(var j = right; j >= left; --j){
console.log("tile_"+output[down][j].match(regexp));
setTimeout(function() { document.getElementById("tile_"+output[down][j].match(regexp)).style.display = "block"; }, 2000);
}
down--;
if(top > down || left > right) {
break;
}
//Starting showing leftmost column
for(var i = down; i >= top; --i){
console.log("tile_"+output[i][left].match(regexp));
setTimeout(function() { document.getElementById("tile_"+output[i][left].match(regexp)).style.display = "block"; }, 2000);
}
left++;
if(top > down || left > right) {
break;
}
}
}
What is going wrong here?
You are trying to use setTimeout as a pause function. That isn't what it does. It just ensures that the contained function is called around the offset time. So, in your code, it is running through all of the divs and scheduling all the calls to occur simultaneously in about 2 seconds.
Couple of possible solutions
(POOR) Rewrite the code to call the timeout functions at (count++)*2000 so they are called successively
(BETTER) Rewrite the code so that the timeout function updates one div and then updates state and sets another timeout to perform the next update (and so on). Basically unwrap your loops into a series of timeout callbacks.
Update: take a look at How do I add a delay in a JavaScript loop?

Categories