在 CSS3 中重新启动动画:比删除元素更好的方法吗?

Posted

技术标签:

【中文标题】在 CSS3 中重新启动动画:比删除元素更好的方法吗?【英文标题】:Restart animation in CSS3: any better way than removing the element? 【发布时间】: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,但我不限于它们。

【问题讨论】:

How do I re-trigger a WebKit CSS animation via javascript?的可能重复 您可以在更新的博客文章中阅读另一种强制重排元素的技术: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'); 【参考方案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】:
    将动画实现为 CSS 描述符 将描述符添加到元素以启动动画 使用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/

参考资料:

https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect/KeyframeEffect https://developer.mozilla.org/en-US/docs/Web/API/Animation

【讨论】:

【参考方案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%;

【讨论】:

以上是关于在 CSS3 中重新启动动画:比删除元素更好的方法吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 jquery transit 中暂停和重新启动 css3 转换

在子元素中添加/删除类时的 CSS3 过渡动画

如何停止requestAnimationFrame方法启动的动画

重新想,重新看——CSS3变形,过渡与动画③

从哈希表中删除条目的最佳方法

CSS3 Animation 控制元素在动画的初始位置开始动画