【问题标题】:Restart animation in CSS3: any better way than removing the element?在 CSS3 中重新启动动画:比删除元素更好的方法吗?
【发布时间】:2011-09-10 05:49:59
【问题描述】:

我有一个 CSS3 动画,需要在单击时重新启动。这是一个显示剩余时间的条形图。我正在使用 scaleY(0) 变换来创建效果。

现在我需要通过将条恢复到 scaleY(1) 并让它再次转到 scaleY(0) 来重新启动动画。 我第一次尝试设置 scaleY(1) 失败了,因为它需要同样的 15 秒才能恢复到全长。即使我将持续时间更改为 0.1 秒,我也需要延迟或链接 scaleY(0) 的分配以完成条形补充。 这么简单的任务感觉太复杂了。

我还发现了一个有趣的技巧,通过从文档中删除元素,然后重新插入它的克隆来重新启动动画: http://css-tricks.com/restart-css-animation/

它有效,但有没有更好的方法来重新启动 CSS 动画? 我正在使用 Prototype 和 Move.js,但我不限于它们。

【问题讨论】:

  • 您可以在更新的博客文章中阅读另一种强制重排元素的技术:element.offsetWidth = element.offsetWidth;
  • 根据您的 CSS-Tricks 链接,我发现克隆是最好的解决方案。
  • TL;DR: e.style.animation = 'none'; e.offsetHeight; e.style.animation = ...; 或者,如果你正在使用类:e.classList.remove('a'); e.offsetHeight; e.classList.add('a');

标签: javascript css


【解决方案1】:

无需超时,使用reflow 应用更改:

function reset_animation() {
  var el = document.getElementById('animated');
  el.style.animation = 'none';
  el.offsetHeight; /* trigger reflow */
  el.style.animation = null; 
}
#animated {
  position: absolute;
  width: 50px; height: 50px;
  background-color: black;
  animation: bounce 3s ease-in-out infinite;
}
@keyframes bounce {
  0% { left: 0; }
  50% { left: calc( 100% - 50px ); }
  100% { left: 0; }
}
<div id="animated"></div>
<button onclick="reset_animation()">Reset</button>

【讨论】:

  • 你也可以通过调用properties/method中的任何一个来触发回流,而不仅仅是offsetHeight
  • 不触发回流会影响性能吗?在性能方面,这与所选答案相比如何?
  • 这不适用于应用了超过 1 个动画的元素。
  • @card100,你能举个例子吗?
  • 这段代码中el.style.animation = null; 的行是什么?
【解决方案2】:

只需通过 JavaScript 将 animation 属性设置为 "none",然后设置将属性更改为 "" 的超时,因此它再次从 CSS 继承。

这里的 Webkit 演示:http://jsfiddle.net/leaverou/xK6sa/ 但是,请记住,在实际使用中,您还应该包括 -moz-(至少)。

【讨论】:

  • 谢谢莉亚。快到了:),如果我将动画更改为仅运行一次,我将无法获得相同的效果。当我单击时,动画不会重新开始。
  • 非常感谢! - 但不幸的是,我无法让它在 Safari 中工作。 Chrome、Edge 和 Firefox 正在按预期工作。我使用以下代码:var anim = jQuery(mutation.target).find(".background.background-image:first").get(0); anim.style.WebkitAnimation = 'none'; anim.style.animation = 'none'; setTimeout(function() { anim.style.WebkitAnimation = ''; anim.style.animation = ''; }, 10); }
  • 不够好,因为有时timeout太长会出现漏洞,timeout太短则不生效。如果您需要在动画仍在播放时重新启动,则不推荐。
  • 现在是 2019 年,vendor prefix 应该不再需要了。
  • 为避免@Eric 描述的超时问题,您可以调用void element.offsetWidth; 来强制在动画属性更改之间进行重排,而不是使用超时。
【解决方案3】:

如果你有一些 css3 动画类,例如 .blink 那么你可以 removeClass 一些元素和 addClass 考虑这个元素setTimeout,点击 1 毫秒。

  $("#element").click(function(){
     $(this).removeClass("blink");

     setTimeout(function(){
       $(this).addClass("blink);
    },1 ///it may be only 1 milisecond, it's enough
  });

【讨论】:

  • 似乎不可靠。 1 ms 计时器对我不起作用,开始使用更高的值(在 Firefox 67 中)。
  • @FabienSnauwaert 感谢您的评论,您可能是对的,因为我在 ~FF 50 上进行了测试
【解决方案4】:
  1. 将动画实现为 CSS 描述符
  2. 将描述符添加到元素以启动动画
  3. 使用animationend 事件处理函数在动画完成时删除描述符,以便下次您想要重新启动动画时再次添加它。

---HTML---

<div id="animatedText">
Animation happens here
</div>

<script>

  function startanimation(element) {

    element.classList.add("animateDescriptor");

    element.addEventListener( "animationend",  function() {

      element.classList.remove("animateDescriptor");    

    } );

  }

</script>


<button onclick="startanimation( document.getElementById('animatedText') )">
Click to animate above text
</button>

---CSS---

@keyframes fadeinout {  
      from { color: #000000; }  
    25% {color: #0000FF; }  
    50% {color: #00FF00; }      
    75% {color: #FF0000; }  
      to { color : #000000; }   
  } 

  .animateDescriptor {  
    animation: fadeinout 1.0s;  
  }     

在这里试试:

https://jsfiddle.net/bigjosh/avp9Lk6r/50/

【讨论】:

  • 使用animationend 事件是个好主意,尽管您并不真的想在动画运行时添加一个新的事件监听器。只需将其添加到 div 一次。
【解决方案5】:

@ZachB 关于 Web Animation API 的回答似乎是“正确的”™ 方式来做到这一点,但不幸的是,它似乎要求您通过 JavaScript 定义动画。但是它引起了我的注意,我发现了一些有用的相关内容:

Element.getAnimations()Document.getAnimations()

截至 2021 年,对它们的支持非常好。

就我而言,我想同时重新启动页面上的所有动画,所以我要做的就是:

const replayAnimations = () => {
  document.getAnimations().forEach((anim) => {
    anim.cancel();
    anim.play();
  });
};

但在大多数情况下,人们可能希望选择他们重新启动的动画...

getAnimations 返回一堆 CSSAnimationCSSTransition 对象,如下所示:

animationName: "fade"
currentTime: 1500
effect: KeyframeEffect
  composite: "replace"
  pseudoElement: null
  target: path.line.yellow
finished: Promise {<fulfilled>: CSSAnimation}
playState: "finished"
ready: Promise {<fulfilled>: CSSAnimation}
replaceState: "active"
timeline: DocumentTimeline {currentTime: 135640.502}

# ...etc

所以你可以使用animationNametarget 属性来选择你想要的动画(虽然有点迂回)。


编辑

这是一个方便的函数,仅使用 Document.getAnimations 可能会更兼容,并使用 TypeScript 进行演示/娱乐:

// restart animations on a given dom element
const restartAnimations = (element: Element): void => {
  for (const animation of document.getAnimations()) {
    if (element.contains((animation.effect as KeyframeEffect).target)) {
      animation.cancel();
      animation.play();
    }
  }
};

【讨论】:

  • 如果所有浏览器都支持这一点,Safari 总是迟到..
  • 请注意,Element.getAnimations()Document.getAnimations() 的支持是不同的。后者似乎受到 Safari 的支持,除了 IE 之外的一切。所以为了更可靠,使用后者,你只需要做一些额外的手动过滤。
【解决方案6】:

你也可以使用 display 属性,只需将 display 设置为 none。

display:none;

并且更改支持它阻止(或您想要的任何其他属性)。

display:block;

使用 JavaScript。

而且效果会非常好。

【讨论】:

    【解决方案7】:

    Animation API 让您可以完全控制播放的时间和内容,并受到所有现代浏览器(Safari 12.1+、Chrome 44+、Firefox 48+、Edge 79+)的支持。

    const effect = new KeyframeEffect(
      el, // Element to animate
      [ // Keyframes
        {transform: "translateY(0%)"}, 
        {transform: "translateY(100%)"}
      ], 
      {duration: 3000, direction: "alternate", easing: "linear"} // Keyframe settings
    );
    
    const animation = new Animation(effect, document.timeline);
    
    animation.play();
    

    演示:https://jsfiddle.net/cstz9L8v/

    参考资料:

    【讨论】:

      【解决方案8】:

      MDN上有一个答案,类似于reflow的做法:

      <div class="box">
      </div>
      
      <div class="runButton">Click me to run the animation</div>
      
      @keyframes colorchange {
        0% { background: yellow }
        100% { background: blue }
      }
      
      .box {
        width: 100px;
        height: 100px;
        border: 1px solid black;
      }
      
      .changing {
        animation: colorchange 2s;
      }
      
      function play() {
        document.querySelector(".box").className = "box";
        window.requestAnimationFrame(function(time) {
          window.requestAnimationFrame(function(time) {
            document.querySelector(".box").className = "box changing";
          });
        });
      }
      

      https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Tips

      【讨论】:

      • requestAnimationFrame 的两个嵌套调用不能保证足够,并且没有技术原因它们必须足够。这在功能上看起来比 setTimeout 更好,但实际上并非如此。
      • @AdamLeggett 你能详细说明一下吗?我最近不得不考虑这样做。
      • 我并不像我希望的那样精通浏览器架构,但我的理解是重新启动动画取决于布局引擎,它与渲染引擎位于不同的线程中。 requestAnimationFrame 等待渲染引擎。技术上最正确的答案是使用animate 函数;不幸的是,第二个在技术上最正确的是void element.offsetWidth
      【解决方案9】:

      在此页面上,您可以阅读有关重新启动元素动画的内容:https://css-tricks.com/restart-css-animation/ 这是我的例子:

       <head>
              <style>
                  @keyframes selectss
      {
          0%{opacity: 0.7;transform:scale(1);} 
          100%{transform:scale(2);opacity: 0;}
      }
              </style>
              <script>
                  function animation()
                  {
                      var elm = document.getElementById('circle');
                      elm.style.animation='selectss 2s ease-out';
                      var newone = elm.cloneNode(true);
                      elm.parentNode.replaceChild(newone, elm);
                  }
              </script>
          </head>
          <body>
              <div id="circle" style="height: 280px;width: 280px;opacity: 0;background-color: aqua;border-radius: 500px;"></div>
              <button onclick="animation()"></button>
          </body>
      

      但如果你愿意,你可以删除元素动画然后返回它:

      function animation()
              {
                  var elm = document.getElementById('circle');
                  elm.style.animation='';
                  setTimeout(function () {elm.style.animation='selectss 2s ease-out';},10)
              }
      

      希望我能帮上忙!

      【讨论】:

        【解决方案10】:

        创建第二个“keyframe@”来重启你的动画,唯一的问题是你不能为重启的动画设置任何动画属性(它只是有点弹回来)

        ----- HTML -----

        <div class="slide">
            Some text..............
            <div id="slide-anim"></div>
        </div><br>
            <button onclick="slider()"> Animation </button>
            <button id="anim-restart"> Restart Animation </button>
        <script>
            var animElement = document.getElementById('slide-anim');
            document.getElementById('anim-restart').addEventListener("mouseup", restart_slider);
        
            function slider() {
                animElement.style.animationName = "slider";             // other animation properties are specified in CSS
            }
            function restart_slider() {
                animElement.style.animation = "slider-restart";         
            }
        </script>
        

        ----- CSS -----

        .slide {
            position: relative;
            border: 3px black inset;
            padding: 3px;
            width: auto;
            overflow: hidden;
        }
        .slide div:first-child {
            position: absolute;
            width: 100%;
            height: 100%;
            background: url(wood.jpg) repeat-x;
            left: 0%;
            top: 0%;            
            animation-duration: 2s;
            animation-delay: 250ms;
            animation-fill-mode: forwards;
            animation-timing-function: cubic-bezier(.33,.99,1,1); 
        }
        
        @keyframes slider {
            to {left: 100%;}
        }
        
        @keyframes slider-restart {
            to {left: 0%;}
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-01-31
          • 2013-01-06
          • 2017-04-28
          • 2017-03-28
          • 1970-01-01
          • 1970-01-01
          • 2022-08-05
          • 1970-01-01
          相关资源
          最近更新 更多