如何保持 animate.css 动画的到达状态?

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

我有一个元素起初是不可见的 (opacity: 0)。
当动画被触发时,它淡入,但由于 opacity 的值似乎被重置,它在完成后再次消失。

如何防止重置动画属性?

我使用 animate.css 4.1.1 和 Google Chrome.



示例上下文:

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;
}

下面的代码使用这个JS函数添加动画类(在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});
  });


这里我尝试触发一个in动画,稍等片刻,然后有一个out动画。
描述的问题表现为闪烁(可见于此jsfiddle。)



我做错了什么?



更新:

我最终使用了答案的修改版本:

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

我建议您在单独的 class 中将 opacity 替换为 transform,并根据动画类型(“in”或“out”)切换 class ") 当 animationend 触发时。

此外,最好在开始动画之前设置事件处理程序

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>

这里有几个问题:

  • 动画 flipInXflipOutX already controls opacity。您不必自己管理它。
  • 从元素中删除动画 class 后,动画效果就会撤消并且其状态会重置。因此,在动画结束后立即删除动画 classes 不会保留结束状态。我们需要在下一个动画开始之前删除旧动画 classes。
  • 元素从水平到垂直进行动画处理。所以我们需要将 CSS 中元素的初始位置设置为 horizo​​ntal.

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>