javascriptanimationflickeranimate.css

How to keep the reached state of an animate.css animation?


I have an element which is invisible at first (opacity: 0).
When an animation is triggered, it fades in, but since the value of opacity seems to get reset, it vanishes again after completion.

How do I prevent a reset of the animated properties?

I use animate.css 4.1.1 with Google Chrome.



Example context:

HTML:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />

<div class="main-container">
  <div id="test1"></div>
</div>

CSS:

#test1 {
  position: absolute;
  
  margin: 10%;
  
  width: 25%;
  height: 25%;
  
  opacity: 0;
  
  background: blue;
}

The code below uses this JS function to add animation classes (found on the animate.css website):

const animateCSS = (element, animation, prefix = 'animate__') =>
  // We create a Promise and return it
  new Promise((resolve, reject) => {
    const animationName = `${prefix}${animation}`;
    const node = document.querySelector(element);

    node.classList.add(`${prefix}animated`, animationName);

    // When the animation ends, we clean the classes and resolve the Promise
    function handleAnimationEnd(event) {
      event.stopPropagation();
      node.classList.remove(`${prefix}animated`, animationName);
      resolve('Animation ended');
    }

    node.addEventListener('animationend', handleAnimationEnd, {once: true});
  });


Here I try to trigger an in animation, wait for a moment and then have an out animation.
The described problem manifests as a flickering (can be seen in this jsfiddle.)



What am I doing wrong?



UPDATE:

I ended up using a modified version of the answer:

function animateCss(node, animationName, duration = 1, prefix = 'animate__') {
  const envCls = `${prefix}animated`;
  const animationCls = `${prefix}${animationName}`;
  
  // Remove all applied animate.css classes.
  node.className = node.className
      .split(" ")
      .filter((cls) => !cls.startsWith(prefix))
      .join(" ");
  
  // Promise resolves when animation has ended.
  return new Promise((resolve, reject) => {
    node.addEventListener('animationend', (event) => {
      event.stopPropagation();
      resolve('Animation ended');
    }, {once: true});
    
    node.style.setProperty('--animate-duration', `${duration}s`);
    node.classList.add(envCls, animationCls);       // Starts CSS animation.
  });
}

// --- Test ---

let test1 = document.getElementById("test1");

// hide the element at first.
animateCss(test1, "fadeOut", 0);

setTimeout(()=>{ 
  animateCss(test1, "flipInX");
}, 1000);

setTimeout(()=>{
  animateCss(test1, "zoomOut");
}, 5000);

Solution

  • Couple of issues here:

    const animateCSS = (element, animation, lastAnim, prefix = 'animate__') => {
      const animationName = `${prefix}${animation}`;
      const node = document.querySelector(element);
    
      // remove the last animation
      node.classList.remove(`${prefix}animated`, `${prefix}${lastAnim}`);
    
      // We create a Promise and return it
      return new Promise((resolve, reject) => {
        node.classList.add(`${prefix}animated`, animationName);
    
        function handleAnimationEnd(event) {
          event.stopPropagation();
          //do not remove the class here
          //node.classList.remove(`${prefix}animated`, animationName);
          resolve('Animation ended');
        }
    
        node.addEventListener('animationend', handleAnimationEnd, { once: true});
      });
    }
    
    // Test ------------------------------------------------------------
    
    let test1 = document.getElementById("test1");
    test1.style.setProperty('--animate-duration', '2s');
    
    setTimeout(() => {
      document.body.appendChild(document.createTextNode('1'));
      animateCSS("#test1", "flipInX");
    }, 1000);
    
    setTimeout(() => {
      document.body.appendChild(document.createTextNode('2'));
      animateCSS("#test1", "flipOutX", "flipInX")
    }, 3000);
    
    setTimeout(() => {
      document.body.appendChild(document.createTextNode('3'));
      animateCSS("#test1", "flipInX", "flipOutX")
    }, 6000);
    
    setTimeout(() => {
      document.body.appendChild(document.createTextNode('4'));
      animateCSS("#test1", "flipOutX", "flipInX");
    }, 9000);
    #test1 {
      position: absolute;
      width: 25vw;
      height: 25vw;
      top:3rem;
      left: 6rem;
      background: blue;
      
      /* starting state */
      -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
      transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    }
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
    
    <div class="main-container">
      <div id="test1"></div>
    </div>