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

Posted

技术标签:

【中文标题】停止无限的 CSS3 动画并平滑恢复到初始状态【英文标题】:Stop infinite CSS3 animation and smoothly revert to initial state 【发布时间】:2016-06-01 03:41:55 【问题描述】:

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

加载器由 4 个盒子组成,它们可以上下动画。我遇到的问题是,当动画应该停止时,框会跳到初始位置。我正在寻找的行为是:加载程序无限动画,直到加载完成,此时它应该动画到初始位置并停止,有点像 animation-iteration-count: infinite 并将其更改为 animation-iteration-count: 1 以停止动画。 (顺便说一句,这不起作用)。

查看这个小提琴以了解我的意思:https://jsfiddle.net/cazacuvlad/qjmhm4ma/(单击停止按钮时,框应该动画到初始位置,而不是跳跃)

基本设置是:

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

为了启动加载器,我将一个包含动画的loader-active 类添加到loader-wrapper

少:

.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 类中的 span 中,不带 loader-active,并在添加 loader-active 时使用 animation-iteration-countanimation-play-state,但没有任何运气。

【问题讨论】:

你不能用 CSS 做到这一点。你需要我怀疑是快速复杂的 javascript 用JS做是备份方案。我认为这是动画的有效用例,应该有一种纯 CSS 方式来做到这一点。 动画很好,但 CSS 无法检测动画在它的(中间)循环中的位置,然后根据条件运行。我什至不确定JS是否可以做到这一点,但如果可以,那就是这样。 这可能不是不可能的,但也不是直截了当的。可以参考this thread。在该答案中,我已通过 vals 链接到另一个答案,该答案显示了实现此类目标的方法。 (注意:我链接到我的答案,而不是直接链接到 vals',因为我的答案中有一些解释,可能对您也有帮助。) 我找到了一个非常简单的解决方法,但并不完全满意,因为它仍然涉及 JS,但效果很好。将其发布为答案,以防其他人偶然发现此问题。 【参考方案1】:

找到了一个非常简单的解决方法。仍然不是纯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);
      
    
  

【讨论】:

这实际上是一个非常好的主意。我喜欢它 :) 我链接的案例更复杂,因为这些案例需要逐渐暂停而不是完成迭代。 不幸的是,如果animation-directionalternate,这将不起作用。我们必须手动结束它。很好的解决方案!一种可能的解决方法是获取当前的animation-duration 并将其与elapsedTime 进行比较。 这正是我所需要的!!仅供参考,作为动画方向的解决方法:交替我只是将我的动画更改为通过 0%、50%、100% 而不是交替来回移动,效果很好【参考方案2】:

您还可以添加一个指定迭代计数的类以停止无限循环。这种方法的优点是您可以更改 durationtiming-function 可以很好地缓和一些动画(例如旋转徽标)。

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

【讨论】:

以上是关于停止无限的 CSS3 动画并平滑恢复到初始状态的主要内容,如果未能解决你的问题,请参考以下文章

CSS3:动画之间的平滑过渡:hover

UIView 动画在转换到新视图之前快速恢复到原始状态

平滑动画停止

CSS3:动画精灵+悬停过渡

无限 SVG 动画在悬停时平滑地检索初始状态

让hover效果平滑过渡回初始状态?