0

On my element, I have a CSS animation running as long as it has a certain class (wiggle), and a transition as soon as it has a different one (right):

<div class="outer">
  <div class="inner wiggle"></div>
</div>
@keyframes wiggle {
  from {
    left: 10%;
  }
  50% {
    left: 30%;
  }
  to {
    left: 10%;
  }
}

.inner {
  left: 0;

  &.wiggle {
    animation: wiggle 2s infinite;
  }

  &.right {
    left: 90%;
    transition: left 2s;
  }
}

Now, if remove the wiggle class and add right at the same time, the transition doesn't play out; left: 90% applies immediately. However, if there's a delay between removing the former and adding the latter, the transition will happen as expected.

Here's a JSFiddle illustrating the issue.

It looks like when coming from an animation, values (such as left in this case) don't have an explicit value to transition from, so they're just rendered to their final state.

Is this expected behavior, i.e. is it part of a specification? Or are browsers free how to handle that case?

I've tested on the lastest versions of Firefox and Chromium.


Clarification: I'm not mainly looking for workarounds, especially not complicated ones, but more for a reason why exactly browsers behave like they do.

Cedric Reichenbach
  • 8,970
  • 6
  • 54
  • 89
  • https://stackoverflow.com/a/33652438/9288348, https://stackoverflow.com/questions/33829469/css-transition-after-animation-ends?noredirect=1&lq=1 I think they had the same problem. – Jeremy Apr 04 '18 at 12:46
  • I think this may be a bug in browser rendering or so, but If you want a solution I can give you one with `transform`.! – Jithin Raj P R Apr 04 '18 at 12:52
  • @Jeremy the first one looks like it's caused by the same behavior, but not exactly the same problem, and the second is slightly different, at least it's solution it's applicable in my case. – Cedric Reichenbach Apr 04 '18 at 12:55
  • @weBBer Well, I'm trying to do similar things with other properties too, `left` was just easiest to visualize and isolate the issue. For example, there's no such workaround for `opacity`. – Cedric Reichenbach Apr 04 '18 at 12:57
  • 1
    @CedricReichenbach I didn't mean the same problem, sorry. But I ment that it both kinda had the same cause. Since you can't have `animation ` and `transition` on the same element. – Jeremy Apr 04 '18 at 12:59

3 Answers3

0

I think this may be a bug in browser rendering or so, but If you want a solution I can give you one alternative method with transform

A working fiddle for you:

$('#toggle').click(() => {
  $('.inner').removeClass('wiggle').addClass('right');
});
@keyframes wiggle {
  from {
    left: 10%;
  }
  50% {
    left: 30%;
  }
  to {
    left: 10%;
  }
}

.outer {
  height: 5em;
  background-color: black;
  position: relative;
}

.inner {
  height: 100%;
  width: 10%;
  position: absolute;
  transform: translate(0%);
  background-color: green;
  transition: transform 2s ease-in-out;
}

.inner.wiggle {
  animation: wiggle 2s infinite;
}

.inner.right {
  transform: translate(900%);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>
  Animated
</h2>
<div class="outer">
  <div class="inner wiggle"></div>
</div>
<h2>
  Not animated
</h2>
<div class="outer">
  <div class="inner"></div>
</div>
<hr>
<button id="toggle">
move right
</button>

I hope this was helpful for you.

Jithin Raj P R
  • 6,667
  • 8
  • 38
  • 69
0

Its look it is the expected behavior. I think when you are adding the right class with left:90%, its not able to pick the starting value for the left css property. As an alternate you can create another keyframe for the .right class

$('#toggle').click(() => {
  $('.inner').removeClass('wiggle').addClass('right');
});
@keyframes wiggle {
  0% {
    left: 10%;
  }
  50% {
    left: 30%;
  }
  100% {
    left: 10%;
  }
}

@keyframes right {
  100% {
    left: 90%;
  }
}

.outer {
  height: 5em;
  background-color: black;
  position: relative;
}

.inner {
  height: 100%;
  width: 10%;
  position: absolute;
  left: 0;
  background-color: green;
  transition: left 2s;
}

.wiggle {
  animation: wiggle 2s infinite;
}

.right {
  animation: right 2s forwards;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>
  Animated
</h2>
<div class="outer">
  <div class="inner wiggle"></div>
</div>
<h2>
  Not animated
</h2>
<div class="outer">
  <div class="inner"></div>
</div>
<hr>
<button id="toggle">
move right
</button>
Bhuwan
  • 16,525
  • 5
  • 34
  • 57
0

This workaround might help you, the idea is to use the css variables, the idea is to set the animation with an alternate function, so in this way we will only need From and To... on the other hand we need to set up and event for every iteration, in that way we know when an iteration ends,

So when we click the button you can change the variable value and remove the class...

$('#toggle').click(() => {
  $('.inner').addClass('right');
});
let flag = false;
$(".inner").on("animationiteration webkitAnimationIteration oAnimationIteration MSAnimationIteration", function(){ 
  flag && $(".inner").removeClass('wiggle');
  if($(".inner").hasClass('right')) flag = true;  
});
:root {
     --from: 10%;
     --to: 30%;
   }
   
   
@keyframes wiggle {
  from {
    left: var(--from);
  }

  to {
    left: var(--to);
  }
}

.outer {
  height: 5em;
  background-color: black;
  position: relative;
}

.inner {
  height: 100%;
  width: 10%;
  position: absolute;
  transform: translate(0%);
  background-color: green;
  transition: transform 2s ease-in-out;
  left:90%;
}

.inner.wiggle {
  left:10%;
  animation: wiggle 1s infinite;
  animation-direction: alternate;
}

.inner.right {
  --to:90%;
  animation-direction: normal;
  animation-duration:2s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>
  Animated
</h2>
<div class="outer">
  <div class="inner wiggle"></div>
</div>
<hr>
<button id="toggle">
move right
</button>

Consider that this has a bug when the animation is moving back to the start point...

Renzo Calla
  • 7,486
  • 2
  • 22
  • 37
  • Hmm, interesting approach; so instead of disabling the animation, we just alter it. However, it's still a workaround and pretty complex. I'm not mainly looking for a perfect workaround, but more for a reason why exactly it behaves as it does. – Cedric Reichenbach Apr 04 '18 at 14:04