How do I wait for this animation to finish before moving on to the next one?
Asked Answered
C

3

5

I am creating a game with HTML, CSS, & JavaScript. I have 5 h2(s) and I want to wait for the animation to finish before moving on to any part of the code. The animation works how I want it to but the JavaScript is starting the next animation before the first one is even done! I have tried using

window.setTimeOut

but no luck. Here is the code with the required code: https://codepen.io/HumanFriend/pen/mdOBvoL Can someone help me?

Cobelligerent answered 22/2, 2021 at 22:43 Comment(0)
Y
2

To elaborate on entio's answer, your animation occurs when an element has the class "typing-effect." When that animation ends, the browser calls an event called "animationend." You can use JavaScript to run your animation on the next element by accessing that event.

Notice in HTML the snippit below, I've moved the "display: hidden" to a CSS class and removed the "typing-effect." In my JavaScript function, I enable the class in the first element, increment the counter, and told the "animationend" to call the function again with the new value for "i."

Edit: I forgot to mention, I modified the id values. I don't believe a valid id value in HTML5 can contain parenthesis.

console.log("script.js connected");

let iLevel = 1;
let loop = 0;

function animateNext(i){
  let stop = document.querySelectorAll('.animated').length;
  let animated = document.querySelector(`#h2_${i}`);
  animated.classList.add('typing-effect');

  animated.addEventListener('animationend', () => {
    if(i===stop){
      iLevel = "iDone";
    }else{
      animateNext(i+1);
    }
  });
}

function startGame() {
  animateNext(1);
}
.animated{
  display: none;
}
.typing-effect {
  display: block;
  animation: typing-effect 5s steps(130, end), 0.75s step-end infinite;
  white-space: nowrap;
  overflow: hidden;
  border-right: 2px solid black;
}
.typing-effect:after {
  content: " ";
}
@keyframes typing-effect {
  0% {
    width: 0%;
  }
  100% {
    width: 100%;
  }
}
@keyframes blink-caret {
  from,
  to {
    border-color: transparent;
  }
  50% {
    border-color: black;
  }
}
<button onclick="startGame();">START GAME</button>

<div id="instructions">

  <div>
    <h2 id="h2_1" class="animated">Welcome to The Number Wizrd</h2>
  </div>

  <div>
    <h2 id="h2_2" class="animated">Adjfosdf</h2>
  </div>
  <div>
    <h2 id="h2_3" class="animated">sosidjfs</h2>
  </div>
  <div>
    <h2 id="h2_4" class="animated">difjspodf</h2>
  </div>
  <div>
    <h2 id="h2_5" class="animated">skidjfosidf</h2>
  </div>

</div>
Yeast answered 22/2, 2021 at 23:37 Comment(2)
Wow! This is exactly what I was looking for! I haven't quite heard/used some of the keywords you used but I'll explore. Thank You!Cobelligerent
Document.querySelector is a little more intuitive IMHO because you can form your selector similar to a CSS selector. Document.querySelectorAll returns a node list of results (similar to an array). Let me know if there are other pieces that need clarification. Also, if this was good enough for your needs, please accept my answer. Happy coding!Yeast
S
7

You can listen to animationend event, fe:

const animated = document.querySelector('.animated');

animated.addEventListener('animationend', () => {
  console.log('Animation ended');
});

Read more: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/animationend_event


Random thing:

When you use setTimeout(nextI_ifelse(), 5000); you just invoke the nextI_ifelse inline (and you're setting timeout for the value returned from this function). Change it to setTimeout(nextI_ifelse, 5000);

Any you just want to read about algorithms in general. What you try to do makes sense, but the way you're trying to achieve that does not. The for loop in your codepen runs instantly, so iLevel is directly set to the last value.

Shopwindow answered 22/2, 2021 at 22:47 Comment(0)
Y
2

To elaborate on entio's answer, your animation occurs when an element has the class "typing-effect." When that animation ends, the browser calls an event called "animationend." You can use JavaScript to run your animation on the next element by accessing that event.

Notice in HTML the snippit below, I've moved the "display: hidden" to a CSS class and removed the "typing-effect." In my JavaScript function, I enable the class in the first element, increment the counter, and told the "animationend" to call the function again with the new value for "i."

Edit: I forgot to mention, I modified the id values. I don't believe a valid id value in HTML5 can contain parenthesis.

console.log("script.js connected");

let iLevel = 1;
let loop = 0;

function animateNext(i){
  let stop = document.querySelectorAll('.animated').length;
  let animated = document.querySelector(`#h2_${i}`);
  animated.classList.add('typing-effect');

  animated.addEventListener('animationend', () => {
    if(i===stop){
      iLevel = "iDone";
    }else{
      animateNext(i+1);
    }
  });
}

function startGame() {
  animateNext(1);
}
.animated{
  display: none;
}
.typing-effect {
  display: block;
  animation: typing-effect 5s steps(130, end), 0.75s step-end infinite;
  white-space: nowrap;
  overflow: hidden;
  border-right: 2px solid black;
}
.typing-effect:after {
  content: " ";
}
@keyframes typing-effect {
  0% {
    width: 0%;
  }
  100% {
    width: 100%;
  }
}
@keyframes blink-caret {
  from,
  to {
    border-color: transparent;
  }
  50% {
    border-color: black;
  }
}
<button onclick="startGame();">START GAME</button>

<div id="instructions">

  <div>
    <h2 id="h2_1" class="animated">Welcome to The Number Wizrd</h2>
  </div>

  <div>
    <h2 id="h2_2" class="animated">Adjfosdf</h2>
  </div>
  <div>
    <h2 id="h2_3" class="animated">sosidjfs</h2>
  </div>
  <div>
    <h2 id="h2_4" class="animated">difjspodf</h2>
  </div>
  <div>
    <h2 id="h2_5" class="animated">skidjfosidf</h2>
  </div>

</div>
Yeast answered 22/2, 2021 at 23:37 Comment(2)
Wow! This is exactly what I was looking for! I haven't quite heard/used some of the keywords you used but I'll explore. Thank You!Cobelligerent
Document.querySelector is a little more intuitive IMHO because you can form your selector similar to a CSS selector. Document.querySelectorAll returns a node list of results (similar to an array). Let me know if there are other pieces that need clarification. Also, if this was good enough for your needs, please accept my answer. Happy coding!Yeast
F
0

It might be worthy of note, that animationend/transitionend events are emitted for every animated element in the element tree you attach the listeners to:

#parent:hover {
  transition: ... 0.5s;
}
#child:hover {
  transition: ... 0.2s;
}
<div id="parent">
  <div id="child">Hover me</div>
<div>
...
parent.addEventListener('animationend', (event) => {
  console.log('animated', event.target);
});
animated div#child
animated div#parent

As such, if you make an animated container that can have animated children, then you should account for event bubbling:

dialog.addEventListener('close', closeHandler);
dialog.addEventListener('transitionend', animationHandler);

let returnValue;
const closeHandler = () => {
  returnValue = ...;
};
const animationHandler = ({ target }) => {
  // make sure it's the dialog that ended animating,
  // and not some button's 'onblur' animation
  if (target === dialog) {
    onClose(returnValue);
  }
};

Fryer answered 25/7, 2024 at 9:6 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.