【问题标题】:How to keep the reached state of an animate.css animation?如何保持 animate.css 动画的到达状态?
【发布时间】:2022-02-06 10:10:06
【问题描述】:

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

如何防止重置动画属性?

我在 Google Chrome 中使用 animate.css 4.1.1。



示例上下文:

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 中看到。)

  • 首先我认为解决方案应该像更改属性一样简单。

    完成后:

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

    马上:

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

  • 然后我认为闪烁是由任何动画延迟引起的。
    所以我禁用了它:

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

    没有任何影响。


我做错了什么?



更新:

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

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

【问题讨论】:

标签: javascript animation flicker animate.css


【解决方案1】:

这里有几个问题:

  • 动画flipInXflipOutXalready controls opacity。您不必自己管理。
  • 从元素中删除动画类后,动画效果将被撤消并重置其状态。因此,在动画结束后立即删除动画类不会保留结束状态。我们需要在下一个动画开始之前删除旧的动画类。
  • 元素从水平动画到垂直动画。所以我们需要将元素在 CSS 中的初始位置设置为水平。

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>

【讨论】:

  • 在我的用例中,动画可以在初始化时更改。从您提供的 github 链接中推断每个动画的起始状态列表是否有任何顾虑? (对于fadeIn -> opacity: 0 等)
  • github 链接指向项目实际的最新源代码,CDN 直接从 github 获取文件(缩小和非缩小)它是自动化的。应该没有问题。只需确保源代码的当前版本与 CDN url 中的版本匹配即可。如果您对旧版本使用 CDN 链接,则在 github 上查看文件的历史记录以查看该版本的文件内容。
【解决方案2】:

我建议您在单独的类中将 opacity 替换为 transform,并在 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>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-21
    • 2013-06-22
    • 2015-02-19
    • 2016-08-20
    • 1970-01-01
    • 2015-12-04
    • 1970-01-01
    • 2012-03-09
    相关资源
    最近更新 更多