CSS animation doesn't restart when resetting class
Asked Answered
B

10

13

I am using CSS shader + animation. My shader class is defined as follows:

.shader{
-webkit-filter :custom(url(v.vs) mix(url(f.fs) multiply destination-over), 200 200);
-webkit-animation-name: test;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: 1
}

I am trying to set/unset the styles(shader+animation) dynamically using jquery through $('#holder').addClass('shader'); and $('#holder').removeClass('shader');

However, the weird thing is when I reset the class (e.g., calling addClass after removeClass), only the shader gets reapplied but the animation doesn't (I have hooked the AnimationStart event to see when my animation starts). Anyone know why this is the case and how I can solve it?

Edit: I added a simplified version of JSfiddle snippet here. Essentially I'm re-applying the animation to a div twice but the actual animation only gets called the first time.

Baccarat answered 17/4, 2013 at 3:24 Comment(6)
Did you try keeping the animation properties in a separate class that is never removed?Razee
mmmm no but I don't think that would help, since I only have 1 iteration and I call removeClass after the animationEnd eventBaccarat
Can you post a jsFiddle so we can have a better lookConover
An example of the animation/filter in action might help.Saviour
I posted a JS fiddle link.Baccarat
Possible duplicate of Restart animation in CSS3: any better way than removing the element?Cryptonymous
B
3

I think I figured it out. According to this, css animation can't get applied to the same node twice (even if you have a different animation!). So I had to clone the node, remove the original, and add back the cloned node.

Baccarat answered 17/4, 2013 at 5:19 Comment(0)
M
29

The problem is that, even though you remove and then re-apply the animated class, you do this in the course of a single, blocking function. When your function exits, it appears to the rendering engine that nothing has changed.

One solution (the one that you chose), is to clone the element and destroy the original. This is fine, but if you had any event bindings to the original element (i think) they they will be destroyed too.

Another approach is to remove the animated class from the element, and then wrap the code that re-applies the class inside of a setTimeout() call with a very small delay, e.g.

$('#holder').removeClass('shader');
setTimeout(
    function(){$('#holder').addClass('shader')}
    , 1);

I've tweaked your jsfiddle to use this approach: http://jsfiddle.net/HuFBN/7/

Monoicous answered 17/5, 2013 at 23:42 Comment(2)
This is the correct answer. Extra points go for explaining why this hack is needed ("When your function exits, it appears to the rendering engine that nothing has changed").Superposition
This worked for me. What I also like about it this too is that you can easily adjust the delay in this approach as when it should start again. thanks!Teacup
M
5

According to a 2011 article on css-tricks.com, triggering a reflow in between removing and adding the class will restart the animation. Example (verbose):

$('#holder').removeClass('shader');
$('#holder').offsetWidth = $('#holder').offsetWidth; // triggers reflow
$('#holder').addClass('shader'); // restarts animation
Moral answered 6/6, 2014 at 15:4 Comment(2)
Apparently this breaks in javascript strict mode.Aluminium
@Aluminium I don't see how this breaks strict mode. Do you have more details?Moral
B
3

I think I figured it out. According to this, css animation can't get applied to the same node twice (even if you have a different animation!). So I had to clone the node, remove the original, and add back the cloned node.

Baccarat answered 17/4, 2013 at 5:19 Comment(0)
T
1

You need to recreate your element.

function resetAnimation(jqNode) {
   var clone = jqNode.clone();
   jqNode.after( clone );
   jqNode.remove();
   jqNode[0] = clone[0];
}
Tambac answered 9/10, 2015 at 19:24 Comment(1)
It's a very useful method to reset animations also. Thanks.Markman
L
1

with this simple code you do it ( you must use to triggers reflow .width() ):

$('#holder').removeClass('shader');
$('#holder').width();
$('#holder').addClass('shader');
Localize answered 6/6, 2021 at 20:55 Comment(0)
A
0

Try using this just after you apply the animation - given that "e" is your animated element:

e.outerHTML = e.outerHTML;
Amiens answered 29/4, 2015 at 15:14 Comment(0)
S
0

way to trigger a reflow:

  element.classList.remove("class");
  element.scrollBy(0, 0);
  element.classList.add("class");

works in strict mode! doesn't require a write operation on a read-only value! doesn't require a whole new function! one line and go! :)

Scurvy answered 28/3, 2019 at 22:4 Comment(0)
W
0

I'd be happy to hear any criticism with my method using requestAnimationFrame as I don't see anyone else using it:

element.classList.add("class");
window.requestAnimationFrame(() => element.classList.remove("class"));
Workbook answered 4/6, 2020 at 18:4 Comment(0)
T
0

Another approach is to remove the class when the animation ends by using an animation callback:

element.addEventListener("webkitAnimationEnd", callfunction,false);
element.addEventListener("animationend", callfunction,false);
element.addEventListener("oanimationend", callfunction,false);

And inside of callfunction you remove the animation class.

Note that this will allow you to restart an animation as long as the previous one has finished, I'm currently unsure of how to interrupt animations, but animation-play-state may be able to work for that.

Turner answered 24/11, 2023 at 2:30 Comment(0)
B
-1

You definitely have to remove class that contains animation and then add it again. It should also work without .offsetWidth . It worked for me. So

$('#id').removeClass('animationClass');
$('#id').addClass('animationClass'); // starts animation again

should do the trick.

Baun answered 10/6, 2017 at 23:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.