Reuse CSS Animation in Reversed Direction (By Resetting the State)

Reuse CSS animation in reversed direction (by resetting the state?)

No, there is no way to restart the animation using CSS alone. You'd have to use JavaScript to remove the animation from the element and then re-apply it to the element (after a delay) for it to restart.

The below is what the W3C's CSS3 Animation Spec says (in a different context, but the point should hold good for this case also):

Note also that changing the value of ‘animation-name’ does not necessarily restart an animation (e.g., if a list of animations are applied and one is removed from the list, only that animation will stop; The other animations will continue). In order to restart an animation, it must be removed then reapplied.

emphasis is mine

This CSS Tricks Article by Chris Coiyer also indicates the same and provides some JS solutions for restarting an animation. (Note: The article has a reference to Oli's dabblet which claims that altering properties like duration, iteration count makes it restart on Webkit but it seems to be outdated as they no longer work on Chrome).


Summary:

While you have already touched upon the following, I am going to re-iterate for completeness sake:

  • Once an animation is applied on the element, it remains on the element until it is removed.
  • UA does keep track of the animation being on the element and whether it has completed or not.
  • When you apply the same animation on :checked (albeit with a different direction), the UA does nothing because the animation already exists on the element.
  • The switch of positions (instantaneous) while clicking the checkbox is because of the transform that is applied within the :checked selector. The animation's presence makes no difference.

Solutions:

As you can see from the below snippet, achieving this with a single animation is pretty complex even when using JavaScript.

var input = document.getElementsByClassName("my-checkbox")[0];
input.addEventListener('click', function() { if (this.checked) { this.classList.remove('my-checkbox'); window.setTimeout(function() { input.classList.add('anim'); input.classList.add('checked'); }, 10); } else { this.classList.remove('anim'); window.setTimeout(function() { input.classList.remove('checked'); input.classList.add('my-checkbox'); }, 10); }});
input {  transform: translate3d(50px, 0, 0);}.my-checkbox {  animation: moveLeft 1s;  animation-direction: reverse;}.checked {  transform: translate3d(0px, 0, 0);}.anim{  animation: moveLeft 1s;}@keyframes moveLeft {  from {    transform: translate3d(50px, 0, 0);  }  to {    transform: translate3d(0px, 0, 0);  }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script><input type="checkbox" class="my-checkbox">

CSS Animation: how to trigger the reverse animation?

Your approach with margin and width to fake a rotation is very interesting, but you can do this much more simply with rotateY

.cards {
width: 800px;
margin:auto;
-webkit-perspective:1000;
}
.card {
width: 200px;
height: 100px;
position: relative;
display: inline-block;
margin: 10px;
-webkit-transition: 1s ease-in;
-webkit-transform-style: preserve-3d;
-webkit-transform:translateZ(1px);
}
.card .frontpage, .card .rearpage, img {
width: 100%;
height: 100%;
position: absolute;
top:0;
left:0;
-webkit-transform-style: preserve-3d;
-webkit-backface-visibility: hidden;
}
.card img {
width: 100%;
height: 100%;
}
.card .rearpage,
.card.opened {
-webkit-transform:rotateY(180deg);
}

Demo

As for the question you asked, you can play animations backwards by using the animation-direction:backwards property, though with CSS toggling animations is hard. Thus, I'd recommend you use a transition instead since it's only a change between two states.

And FYI just in case, CSS selector don't always have to be in the parent child format. In your case applying just .child will do the same. The parent child selector is only necessary when needing to a higher selector specificity than existing properties.

Oh, and also FYI, jQuery isn't needed for this. I included an (untested) javascript equivalent if you want. If this is the only place where you're using jQuery on your page I'd recommend not using it because loading the whole jQuery library takes some time and data.

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

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

Combine multiple animations

Can I combine these two animations?

I assume by combine you mean producing forward (on click of add animation) and reverse (on click of remove animation) animations using the same keyframe rules. It's possible to achieve but for that both the forward and reverse animations should be exactly the same (but in opposite directions). When it is same, we can use animation-direction: reverse to achieve reverse effect with same keyframes.

Here, the forward animation has a transform change whereas the reverse doesn't and hence adding animation-direction: reverse would not produce the same effect as the original snippet. Moreover, coding it is not as easy as just adding a property also, a lot of work is needed like mentioned here.


What is the reason for the other two issues?

The reason for both the issues (that is, the element getting opacity: 1 immediately when the remove button is clicked and element getting full size when remove button is clicked while forward animation is still happening) are the same. When you remove the animation on an element (by removing the class) it immediately snaps to the size specified outside of the animation.

For the first case, the size is the one that is mentioned under .element (as .one is removed) and its opacity is default 1 because there is no opacity setting in it. For the second case, when the .one is removed and .two is added, the animation is removed and so the element's size is as specified in .element and the opacity is as specified in .two (because that is later in CSS file).


So what else is the alternate?

When both forward and reverse effects are required and the animation doesn't have any intermediate states (that is, there is only a start state and an end state) then it is better to use transitions instead of animations. The reason is because transitions automatically produce the reverse effect on removal of the class (unlike animations where the reverse animation needs to be written as a separate keyframe and added to the element).

Below is a sample snippet showing how you can achieve a similar effect using just one class without the need for writing keyframes.

 var theBut = document.getElementById('butt'); var theBut2 = document.getElementById('butt2'); theBut.addEventListener('click', function a() {   document.querySelector('.element').classList.add('one'); });
theBut2.addEventListener('click', function b() { document.querySelector('.element').classList.remove('one'); });
.element {  background-color: #d91e57;  display: block;  width: 160px;  height: 160px;  border-radius: 90%;  transform: scale(0.25);  opacity: 0;  transition: opacity 2s, transform .1s 2s;}.one {  transform: scale(1);  opacity: 0.5;  transition: all 2s;}
<div class="element"></div><button id="butt">add animation</button><button id='butt2'>remove animation</button>

CSS3 Keyframe Animation issue with timelines

Original Answer:

You need to remove the previous classes also before adding the new ones. When you don't remove the other class before adding a new one, each of them try to set an animation on the same element and as is always the case with CSS, the selector (class) which is defined later in the CSS wins.

var btnCorrect = document.getElementById('btnCorrect');var btnWrong = document.getElementById('btnWrong');var ladyDefault = document.getElementById('ladyDefault');var ladyCorrect = document.getElementById('ladyCorrect');var ladyWrong = document.getElementById('ladyWrong');
btnCorrect.addEventListener('click', function(event) { ladyDefault.classList.add('hideLadyDefault'); ladyWrong.classList.remove('showLadyWrong'); ladyWrong.classList.add('hideLadyWrong'); ladyCorrect.classList.remove('hideLadyCorrect'); ladyCorrect.classList.add('showLadyCorrect');});btnWrong.addEventListener('click', function(event) { ladyDefault.classList.add('hideLadyDefault'); ladyCorrect.classList.remove('showLadyCorrect'); ladyCorrect.classList.add('hideLadyCorrect'); ladyWrong.classList.remove('hideLadyWrong'); ladyWrong.classList.add('showLadyWrong');});
#ladyDefault,#ladyCorrect,#ladyWrong {  width: 100px;  height: 150px;  display: inline-block;  margin: 5px;}#ladyDefault {  background-color: blue;}#ladyCorrect {  background-color: green;  opacity: 0.1;}#ladyWrong {  background-color: red;  opacity: 0.1;}#btnCorrect,#btnWrong {  height: 50px;  width: 100px;  display: inline-block;  margin: 5px;}#btnCorrect {  background-color: lime;}#btnWrong {  background-color: darkred;}/*--------------------------- lady default*/
@keyframes hideLadyDefault { 0% { opacity: 1; } 100% { opacity: 0.1; }}.hideLadyDefault { animation-name: hideLadyDefault; animation-duration: 0; animation-iteration-count: 1; animation-direction: normal; animation-fill-mode: forwards; animation-delay: 0;}/*--------------------------- lady correct*/
@keyframes showLadyCorrect { 0% { opacity: 0.1; } 100% { opacity: 1; }}.showLadyCorrect { animation-name: showLadyCorrect; animation-duration: 1s; animation-iteration-count: 1; animation-direction: normal; animation-fill-mode: forwards; animation-delay: 0;}@keyframes hideLadyCorrect { 0% { opacity: 1; } 100% { opacity: 0.1; }}.hideLadyCorrect { animation-name: hideLadyCorrect; animation-duration: 0; animation-iteration-count: 1; animation-direction: normal; animation-fill-mode: both animation-delay: 0;}/*--------------------------- lady wrong*/
@keyframes showLadyWrong { 0% { opacity: 0.1; } 100% { opacity: 1; }}.showLadyWrong { animation-name: showLadyWrong; animation-duration: 1s; animation-iteration-count: 1; animation-direction: normal; animation-fill-mode: forwards; animation-delay: 0;}@keyframes hideLadyWrong { 0% { opacity: 1; } 100% { opacity: 0.1; }}.hideLadyWrong { animation-name: hideLadyWrong; animation-duration: 0; animation-iteration-count: 1; animation-direction: normal; animation-fill-mode: both; animation-delay: 0;}
<div id="ladyDefault">ladyDefault</div><div id="ladyCorrect">ladyCorrect</div><div id="ladyWrong">ladyWrong</div>
<div id="btnCorrect">btnCorrect</div><div id="btnWrong">btnWrong</div>

CSS Animation Fill Mode - What have I done wrong?

So the root issue was the class with the animation was being removed.

I couldn't get it to work using the same keyframes, but what i did was create a new keyframes for counter clockwise, and then removed the opposite class when the buttons were clicked

Changes:

css

.spin-clockwise {


-moz-animation: spin 2s;
-webkit-animation: spin 2s;

}

.spin-fill-mode {
-moz-animation-fill-mode: forwards;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}

.spin-counter-clockwise {
-moz-animation: spin-counter 2s;
-webkit-animation: spin-counter 2s;

}

@keyframes spin {
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
}
}

@keyframes spin-counter {
from {
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
}
to {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
}

js:

$('#btnb').on('click', (e)->


$('.box')
.addClass('spin-counter-clockwise')
.removeClass('spin-clockwise')
)

$('#btnf').on('click', (e) ->
$('.box')
.addClass('spin-clockwise')
.removeClass('spin-counter-clockwise')
)

And add the class spin-fill-mode to box. Though you could probably just leave the fill-mode in the animation classes...
updated codepen:

http://codepen.io/anon/pen/QypvOr

Repeat css animation with checkbox

I've been trying to solve your issue with just one keyframe declaration.

As DarkFalcon points out you can solve it by declaring two different keyframes and apply one for the :checked and the other for the initial state.

Code Snippet

@keyframes aaa {  to {    background: red;  }}@keyframes bbb {  to {    background: red;  }}
input:checked + div { animation-name: bbb;}div { width: 100px; height: 100px; background:blue; animation-fill-mode: forwards; animation-name: aaa; animation-duration: 1s;}
<input type="checkbox" /><div></div>

Inverting animation direction

You can use flexbox like this:

#slides{  
position:relative;
overflow:hidden;
margin:0 auto;
background:#cf5;
width:400px;
height:200px;
display:flex;
flex-direction:row-reverse;
}
#slides div{
flex:0 0 100%;
position:relative;
background:#eee;
width:400px;
height:200px;
}

And then on your JS just change the scrollLeft value to be animated first to 0 and after to slideW.

Updated Fiddle



Related Topics



Leave a reply



Submit