Restart animation in CSS3: any better way than removing the element?

JavascriptCss

Javascript Problem Overview


I have a CSS3 animation that needs to be restarted on a click. It's a bar showing how much time is left. I'm using the scaleY(0) transform to create the effect.

Now I need to restart the animation by restoring the bar to scaleY(1) and let it go to scaleY(0) again. My first attempt to set scaleY(1) failed because it takes the same 15 seconds to bring it back to full length. Even if I change the duration to 0.1 second, I would need to delay or chain the assignment of scaleY(0) to let the bar replenishment complete. It feels too complicated for such a simple task.

I also found an interesting tip to restart the animation by removing the element from the document, and then re-inserting a clone of it: http://css-tricks.com/restart-css-animation/

It works, but is there a better way to restart a CSS animation? I'm using Prototype and Move.js, but I'm not restricted to them.

Javascript Solutions


Solution 1 - Javascript

No need in timeout, use reflow to apply the change:

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>

Solution 2 - Javascript

Just set the animation property via JavaScript to "none" and then set a timeout that changes the property to "", so it inherits from the CSS again.

Demo for Webkit here: http://jsfiddle.net/leaverou/xK6sa/ However, keep in mind that in real world usage, you should also include -moz- (at least).

Solution 3 - Javascript

  1. Implement the animation as a CSS descriptor
  2. Add the descriptor to an element to start the animation
  3. Use a animationend event handler function to remove the descriptor when the animation completes so that it will be ready to be added again next time you want to restart the animation.

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

Try it here:

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

Solution 4 - Javascript

If you have some class for css3 animation, for exapmle .blink then you can removeClass for some element and addClass for this element thought setTimeout with 1 milisecond by click.

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

Solution 5 - Javascript

@ZachB's answer about the Web Animation API seems like "right"™ way to do this, but unfortunately seems to require that you define your animations through JavaScript. However it caught my eye and I found something related that's useful:

Element.getAnimations() and Document.getAnimations()

The support for them is pretty good as of 2021.

In my case, I wanted to restart all the animations on the page at the same time, so all I had to do was this:

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

But in most cases people will probably want to select which animation they restart...

getAnimations returns a bunch of CSSAnimation and CSSTransition objects that look like this:

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

So you could use the animationName and target properties to select just the animations you want (albeit a little circuitously).


EDIT

Here's a handy function that might be more compatible using just Document.getAnimations, with TypeScript thrown in for demonstration/fun:

// 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();
    }
  }
};

Solution 6 - Javascript

You can also use display property, just set the display to none.

display:none;

and the change backs it to block (or any other property you want).

display:block;

using JavaScript.

and it will work amazingly.

Solution 7 - Javascript

There is an answer on MDN, which is similar to the reflow approach:

<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

Solution 8 - Javascript

The Animation API gives you full control over when and what to play, and is supported by all modern browsers (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();

Demo: https://jsfiddle.net/cstz9L8v/

References:

Solution 9 - Javascript

on this page you can read about restart the element animation: https://css-tricks.com/restart-css-animation/ here is my example:

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

but if you want to you can just remove the element animation and then return it:

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

hope i helped!

Solution 10 - Javascript

Create a second "keyframe@" which restarts you animation, only problem with this you cannot set any animation properties for the restarting animation (it just kinda pops back)

----- 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%;}
}

Solution 11 - Javascript

Note that with react, clearing the animation like this, a codesandbox I found, helps

https://codesandbox.io/s/63dsx?file=/src/index.js:240-454

Example I used in my code

function MyComponent() {
  const [shouldTransition, setShouldTransition] = useState(true);

  useEffect(() => {
    setTimeout(() => {
      // in my code, I change a background image here, and call this hook restart then animation,
      // which first clears the animationName
      setShouldTransition(false);
    }, timeout * 1000);
  }, [curr]);

  useEffect(() => {
    // then restore the animation name after it was cleared
    if (shouldTransition === false) {
      setShouldTransition(true);
    }
  }, [shouldTransition]);
  return (
    <div
      ref={ref2}
      style={{
        animationName: shouldTransition ? "zoomin" : "",
      }}
    />
  );
}

Solution 12 - Javascript

    setInterval(() => {
        $('#XMLID_640_').css('animation', 'none')

        setTimeout(() => {
            $('#XMLID_640_').css('animation', '')
        }, 3000)
    }, 13000)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLeoView Question on Stackoverflow
Solution 1 - JavascriptuserView Answer on Stackoverflow
Solution 2 - JavascriptLea VerouView Answer on Stackoverflow
Solution 3 - JavascriptbigjoshView Answer on Stackoverflow
Solution 4 - JavascriptViktor MezhenskyiView Answer on Stackoverflow
Solution 5 - JavascriptV. RubinettiView Answer on Stackoverflow
Solution 6 - JavascriptUsama MajidView Answer on Stackoverflow
Solution 7 - JavascriptSava B.View Answer on Stackoverflow
Solution 8 - JavascriptZachBView Answer on Stackoverflow
Solution 9 - JavascriptRon CohenView Answer on Stackoverflow
Solution 10 - JavascriptPall ArpadView Answer on Stackoverflow
Solution 11 - JavascriptColin DView Answer on Stackoverflow
Solution 12 - JavascriptCyberT33NView Answer on Stackoverflow