1

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.)

  • First I thought that the solution should be as easy as changing the property.

    after completion:

    let test1 = document.getElementById("test1");
    
    animateCSS("#test1", "flipInX")        // flickers
        .then(()=>{
            test1.style.opacity = 1;
        });
    
    ...
    
    animateCSS("#test1", "flipOutX")
        .then(()=>{
            test1.style.opacity = 0;
        });
    

    immediately:

    let test1 = document.getElementById("test1");
    
    
    animateCSS("#test1", "flipInX")
    test1.style.opacity = 1;
    
    ...
    
    animateCSS("#test1", "flipOutX")       // flickers
    test1.style.opacity = 0;
    

  • Then I thought that the flickering is caused by any animation delay.
    So I disabled that:

    test1.style.setProperty('--animate-delay', '0s');`
    document.documentElement.style.setProperty('--animate-delay', '0s');
    

    Without any effect.


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);
AFoeee
  • 731
  • 7
  • 24
  • [This question](https://stackoverflow.com/questions/60073916/animate-css-animation-disappears-after-animation-is-complete) is similiar, but doesn't have a real answer. – AFoeee Jan 30 '22 at 21:56
  • The answers of [this question](https://stackoverflow.com/questions/49328200/css-animation-flickering-tried-all-tricks-i-could-find) didn't work for me either. – AFoeee Jan 30 '22 at 23:59

2 Answers2

2

Couple of issues here:

  • Animations flipInX, and flipOutX already controls opacity. You don't have to manage it yourself.
  • As soon as you remove the animation class from an element, animation effect are undone and its state resets. So removing animation classes immediately after animation end doesn't preserve the end state. We need to remove the old animation classes before next animation starts.
  • The element animates from horizontal to vertical. So we need to set initial position of the element in the CSS as horizontal.

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>
the Hutt
  • 16,980
  • 2
  • 14
  • 44
  • In my usecase the animations can be changed at the initialization time. Are there any concerns about deducing a list of starting states per animation from the github link you gave? (For `fadeIn` -> `opacity: 0` etc.) – AFoeee Feb 02 '22 at 13:23
  • The github link is to the projects actual latest source code, CDNs pick up files(minified and non-minified) directly from the github it's automated. There should be no issue. Just make sure the source code's current version and the version in CDN url matches. If you are using CDN link for old versions then on github see the file's history to see the file contents at that version. – the Hutt Feb 02 '22 at 13:51
1

I'd suggest you replacing opacity with transform in a separate class, and toggle that class depending on animation type ("in" or "out") when animationend fires.

Also it's better to set event handler before starting the animation.

const animateCSS = (element, animation, prefix = 'animate__') =>
  new Promise((resolve, reject) => {
    const animationName = `${prefix}${animation}`;
    const node = document.querySelector(element);

    const handleAnimationEnd = event => {
      event.stopPropagation();
      node.classList.remove(`${prefix}animated`, animationName);
      node.classList.toggle('out', /out/i.test(animation));
    }

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

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

  });

// Test ------------------------------------------------------------

setTimeout(() => animateCSS("#test1", "flipInX"), 1000);
setTimeout(() => animateCSS("#test1", "flipOutX"), 3000);
setTimeout(() => animateCSS("#test1", "flipInX"), 6000);
setTimeout(() => animateCSS("#test1", "flipOutX"), 9000);
#test1 {
  position: absolute;
  width: 75%;
  height: 75%;
  background: blue;
}

.out {
  transform:translateX(-9999vw)
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />

<div id="test1" class="out"></div>
Kosh
  • 16,966
  • 2
  • 19
  • 34