停止无限的CSS3动画并平滑地恢复到初始状态。

5

我在使用关键帧动画来构建CSS3加载器时遇到了一些问题。

这个加载器由4个方框组成,可以上下移动。我遇到的问题是当动画应该停止时,方框会跳回初始位置。我想要的行为是:在加载完成前,加载器无限制地进行动画,然后在动画结束时以初始位置进行动画并停止,就像将animation-iteration-count: infinite更改为animation-iteration-count: 1来停止动画(这种方法不起作用)。

请查看此 fiddle 示例以了解我的意思:https://jsfiddle.net/cazacuvlad/qjmhm4ma/ (单击“停止”按钮时,这些方框应该动画到初始位置而不是跳动)

基本设置如下:

<div class="loader-wrapper"><span></span><span></span><span></span><span></span></div>

为了启动加载器,我会给loader-wrapper添加一个包含动画的loader-active类。 LESS:
.loader-wrapper {
  &.loader-active {
    span {
      .animation-name(loader);
      .animation-duration(1200ms);
      .animation-timing-function(ease-in-out);
      .animation-play-state(running);
      .animation-iteration-count(infinite);

      &:nth-child(1) {
      }
      &:nth-child(2) {
        .animation-delay(300ms);
      }
      &:nth-child(3) {
        .animation-delay(600ms);
      }
      &:nth-child(4) {
        .animation-delay(900ms);
      }
    }
  }
}

我尝试在loader-wrapper类的中添加动画,但没有添加loader-active的情况下,并且在添加loader-active时调整animation-iteration-countanimation-play-state,但没有成功。


你不能用CSS做到那个。你需要使用我怀疑会很复杂的JavaScript。 - Paulie_D
在JS中做这件事是备选方案。我认为这是动画的有效用例,应该有一种纯CSS的方法来实现它。 - Vlad Cazacu
动画效果很好,但是CSS无法检测动画在其(中)周期中的位置,然后根据条件运行下去。我甚至不确定JS是否能够做到这一点,但如果可以的话,那将是解决问题的方法。 - Paulie_D
1
这可能不是不可能,但也不会很简单。您可以参考此线程。在那个答案中,我链接到了vals的另一个答案,展示了一种实现类似效果的方法。(*注意:*我链接到了我的答案而不是直接链接到vals的答案,因为我的答案中有一些解释,这对您也可能有帮助。) - Harry
1
我找到了一个相当简单的解决方法,虽然它仍然涉及JS,但我并不完全满意,但它可以很好地工作。如果有人也遇到这个问题,我会将其发布为答案。 - Vlad Cazacu
2个回答

8

发现了一个相当简单的解决方法。虽然不是纯CSS,它涉及一点JS,但它的效果很好。

更新的代码片段:https://jsfiddle.net/cazacuvlad/qjmhm4ma/2/

我的做法是将loader-active类移动到每个标签(而不是包装器),并在每个上侦听animationiteration事件,然后停止动画。

$('.loader-wrapper span').on('animationiteration webkitAnimationIteration', function () {
  var $this = $(this);

  $this.removeClass('loader-active');

  $this.off();
});

这基本上是在迭代周期的最后停止动画。
更新了LESS。
.loader-wrapper {
  span {
    &.loader-active {
      .animation-name(loader);
      .animation-duration(1200ms);
      .animation-timing-function(ease-in-out);
      .animation-play-state(running);
      .animation-iteration-count(infinite);

      &:nth-child(1) {
      }
      &:nth-child(2) {
        .animation-delay(300ms);
      }
      &:nth-child(3) {
        .animation-delay(600ms);
      }
      &:nth-child(4) {
        .animation-delay(900ms);
      }
    }
  }
}

那是一个非常好的想法。我喜欢它 :) 我所链接的案例更为复杂,因为它们需要逐步暂停而不是完成一个迭代。 - Harry
不幸的是,如果“animation-direction”为“alternate”,则此方法无效。我们必须手动结束它。虽然这个解决方案非常好!一个可能的解决方法是获取当前的“animation-duration”并将其与“elapsedTime”进行比较。 - yckart
这正是我需要的!!顺便提一下,关于animation-direction:alternate的解决方法是,我通过将动画改为通过0%,50%,100%前进和后退而不是交替来进行,效果非常好。 - milpool

1

您还可以添加一个类,指定迭代计数以停止无限循环。这种方法的优点是您可以更改持续时间时间函数,这对于缓解某些动画(例如旋转标志)非常有用。

.animate-end {
  animation-iteration-count: 3;
  animation-duration: 1s;
  animation-timing-function: ease-out;
}

我们可以使用js添加这个类,它将在计数3时停止动画。
document.querySelector(".loader-wrapper").classList.add("animate-end");

但你也可以通过计数来结束当前的迭代,并使用JS动态地改变元素的样式。

let iterationCount = 0;
document.querySelector(".loader-wrapper span").addEventListener('animationiteration', () => {
//count iterations
  iterationCount++;
});    

yourElement.style.animationIterationCount = iterationCount + 1;

这是一个使用您的代码的演示:

document.querySelector("#start_loader").addEventListener("click", function(){
  document.querySelector(".loader-wrapper").classList.add("loader-active");  
})


let iterationCount = 0;
document.querySelector(".loader-wrapper span").addEventListener('animationiteration', () => {
//count iterations
  iterationCount++;
  console.log(`Animation iteration count: ${iterationCount}`);
});

document.querySelector("#stop_loader").addEventListener("click", function(){
    
    //For some animation it can be nice to change the duration or timing animation
    document.querySelector(".loader-wrapper").classList.add("animate-end");
    
    //End current iteration
     document.querySelectorAll(".loader-wrapper span").forEach(element => {
        element.style.animationIterationCount = iterationCount + 1;
    });


    //Remove Classes with a timeout or animationiteration event
    setTimeout(() => {
        document.querySelector(".loader-wrapper").classList.remove("loader-active");
         document.querySelector(".loader-wrapper").classList.remove("animate-end");
     }, 1200);
    

})
@-moz-keyframes 'loader' {
  0% {
    -moz-transform: translate3d(0, 0, 0);
  }
  50% {
    -moz-transform: translate3d(0, -10px, 0);
  }
  100% {
    -moz-transform: translate3d(0, 0, 0);
  }
}

@-webkit-keyframes 'loader' {
  0% {
    -webkit-transform: translate3d(0, 0, 0);
  }
  50% {
    -webkit-transform: translate3d(0, -10px, 0);
  }
  100% {
    -webkit-transform: translate3d(0, 0, 0);
  }
}

@-o-keyframes 'loader' {
  0% {
    -o-transform: translate3d(0, 0, 0);
  }
  50% {
    -o-transform: translate3d(0, -10px, 0);
  }
  100% {
    -o-transform: translate3d(0, 0, 0);
  }
}

@keyframes 'loader' {
  0% {
    transform: translate3d(0, 0, 0)
  }
  50% {
    transform: translate3d(0, -10px, 0)
  }
  100% {
    transform: translate3d(0, 0, 0)
  }
}
.loader-wrapper {
  margin-bottom: 30px;
}


.loader-wrapper.loader-active span {
  -webkit-animation-name: loader;
  -moz-animation-name: loader;
  -ms-animation-name: loader;
  -o-animation-name: loader;
  animation-name: loader;
  -webkit-animation-duration: 1200ms;
  -moz-animation-duration: 1200ms;
  -ms-animation-duration: 1200ms;
  -o-animation-duration: 1200ms;
  -webkit-animation-timing-function: ease-in-out;
  -moz-animation-timing-function: ease-in-out;
  -ms-animation-timing-function: ease-in-out;
  -o-animation-timing-function: ease-in-out;
  animation-timing-function: ease-in-out;
  -webkit-animation-play-state: running;
  -moz-animation-play-state: running;
  -ms-animation-play-state: running;
  -o-animation-play-state: running;
  animation-play-state: running;
  -webkit-animation-iteration-count: infinite;
  -moz-animation-iteration-count: infinite;
  -ms-animation-iteration-count: infinite;
  -o-animation-iteration-count: infinite;
  animation-iteration-count: infinite;
}


.loader-wrapper.animate-end span {
    /* Works great for some animations */
    /*animation-iteration-count: 1;*/
    /*animation-duration: 1s;*/
}

.loader-wrapper.loader-active span:nth-child(1) {}

.loader-wrapper.loader-active span:nth-child(2) {
  animation-delay: 300ms;
}

.loader-wrapper.loader-active span:nth-child(3) {
  animation-delay: 600ms;
}

.loader-wrapper.loader-active span:nth-child(4) {
  animation-delay: 900ms;
}

.loader-wrapper span {
  margin-right: 5px;
  display: inline-block;
  vertical-align: middle;
  background: black;
  width: 10px;
  height: 10px;
}
<div class="loader-wrapper"><span></span><span></span><span></span><span></span></div>

<button id="start_loader">Start</button>
<button id="stop_loader">Stop</button>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接