最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - CSS Animation Fill Mode - What have I done wrong? - Stack Overflow

programmeradmin1浏览0评论

I need to create a rotation animation. A click event spins an element 180° to point down. Another click event spins the same element back to 0° to point up.

I have animation-fill-mode to set to forwards to preserve the last keyframe state. But it does not appear to be working. All visual elements reset to the default state.

Any ideas what I may be doing wrong?

My Codepen:

My code:

(function() {
  $('#btnb').on('click', function(e) {
    return $('.box').addClass('spin-counter-clockwise');
  });
  $('#btnf').on('click', function(e) {
    return $('.box').addClass('spin-clockwise');
  });
  $('.box').on('webkitAnimationEnd', function(e) {
    return $(e.target).removeClass('spin-counter-clockwise').removeClass('spin-clockwise');
  });
}.call(this));
.box {
  background: red;
  position: relative;
  width: 176px;
  height: 176px;
  margin: 40px auto;
  text-align: center;
}
.box .top {
  background: green;
  position: absolute;
  top: 0;
  width: 100%;
  height: 28px;
}
.box .bottom {
  background: purple;
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 28px;
}
.box .caret {
  color: white;
  font-size: 88px;
  position: absolute;
  top: 42px;
  left: 50px;
}
.spin-clockwise {
  -moz-animation: spin 2s;
  -webkit-animation: spin 2s;
  -moz-animation-fill-mode: forwards;
  -webkit-animation-fill-mode: forwards;
}
.spin-counter-clockwise {
  -moz-animation: spin 2s reverse;
  -webkit-animation: spin 2s reverse;
  -moz-animation-fill-mode: forwards;
  -webkit-animation-fill-mode: forwards;
}
@keyframes spin {
  from {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  to {
    -webkit-transform: rotate(180deg);
    transform: rotate(180deg);
  }
}
body {
  text-align: center;
  margin: 0 auto;
}
<link href=".5.0/css/font-awesome.min.css" rel="stylesheet" />
<script src=".1.1/jquery.min.js"></script>
<h2>Keyframes are not supported in IE9 and earlier</h2>

<div class="box">
  <div class="top">top</div>
  <div class="caret"><i class="fa fa-caret-square-o-up"></i>
  </div>
  <div class="bottom">bottom</div>
</div>

<p>
  <button id="btnf">SPIN CLOCKWISE</button>
</p>
<p>
  <button id="btnb">SPIN COUTNER CLOCKWISE</button>
</p>

I need to create a rotation animation. A click event spins an element 180° to point down. Another click event spins the same element back to 0° to point up.

I have animation-fill-mode to set to forwards to preserve the last keyframe state. But it does not appear to be working. All visual elements reset to the default state.

Any ideas what I may be doing wrong?

My Codepen: http://codepen.io/simspace-dev/pen/RrpGmP

My code:

(function() {
  $('#btnb').on('click', function(e) {
    return $('.box').addClass('spin-counter-clockwise');
  });
  $('#btnf').on('click', function(e) {
    return $('.box').addClass('spin-clockwise');
  });
  $('.box').on('webkitAnimationEnd', function(e) {
    return $(e.target).removeClass('spin-counter-clockwise').removeClass('spin-clockwise');
  });
}.call(this));
.box {
  background: red;
  position: relative;
  width: 176px;
  height: 176px;
  margin: 40px auto;
  text-align: center;
}
.box .top {
  background: green;
  position: absolute;
  top: 0;
  width: 100%;
  height: 28px;
}
.box .bottom {
  background: purple;
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 28px;
}
.box .caret {
  color: white;
  font-size: 88px;
  position: absolute;
  top: 42px;
  left: 50px;
}
.spin-clockwise {
  -moz-animation: spin 2s;
  -webkit-animation: spin 2s;
  -moz-animation-fill-mode: forwards;
  -webkit-animation-fill-mode: forwards;
}
.spin-counter-clockwise {
  -moz-animation: spin 2s reverse;
  -webkit-animation: spin 2s reverse;
  -moz-animation-fill-mode: forwards;
  -webkit-animation-fill-mode: forwards;
}
@keyframes spin {
  from {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  to {
    -webkit-transform: rotate(180deg);
    transform: rotate(180deg);
  }
}
body {
  text-align: center;
  margin: 0 auto;
}
<link href="https://maxcdn.bootstrapcdn./font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>Keyframes are not supported in IE9 and earlier</h2>

<div class="box">
  <div class="top">top</div>
  <div class="caret"><i class="fa fa-caret-square-o-up"></i>
  </div>
  <div class="bottom">bottom</div>
</div>

<p>
  <button id="btnf">SPIN CLOCKWISE</button>
</p>
<p>
  <button id="btnb">SPIN COUTNER CLOCKWISE</button>
</p>

Share Improve this question edited Jan 6, 2016 at 6:22 Harry 89.9k26 gold badges214 silver badges223 bronze badges asked Jan 5, 2016 at 23:18 Chris SimeoneChris Simeone 1,2052 gold badges16 silver badges29 bronze badges 3
  • Is there any specific reason why you've used animations? It would be much easier to achieve this with transitions instead of animation. – Harry Commented Jan 6, 2016 at 4:29
  • 1 Thanks Harry, I will look into transitions. – Chris Simeone Commented Jan 6, 2016 at 6:23
  • 1 Thanks for the great answers Harry, Chris and oMiKeY! If it turns out I use transitions (which sounds like the better method), I'm still glad I attempted this route for the learning experience. I never messed around with CSS animations prior to this and I learned much. – Chris Simeone Commented Jan 6, 2016 at 6:34
Add a ment  | 

3 Answers 3

Reset to default 3

TL;DR: (straight to the suggested solution)

If all you need is a rotation from 0° to 180° on the click of one button and back from 180° to 0° on the other then I would suggest using transitions instead of animations. Transitions by default produce the reverse effect and so there is no need to code for two different states (which makes it even better).

(function() {
  $('#btnb').on('click', function(e) {
    return $('.box').removeClass('spin');
  });
  $('#btnf').on('click', function(e) {
    return $('.box').addClass('spin');
  });
}.call(this));
.box {
  background: red;
  position: relative;
  width: 176px;
  height: 176px;
  margin: 40px auto;
  text-align: center;
  transition: transform 1s linear;
  /* add this to enable transition */
}
.box .top {
  background: green;
  position: absolute;
  top: 0;
  width: 100%;
  height: 28px;
}
.box .bottom {
  background: purple;
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 28px;
}
.box .caret {
  color: white;
  font-size: 88px;
  position: absolute;
  top: 42px;
  left: 50px;
}
.spin {
  /* this is the only thing required for rotation (along with the JS) */
  transform: rotate(180deg);
}
body {
  text-align: center;
  margin: 0 auto;
}
<link href="https://maxcdn.bootstrapcdn./font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>Keyframes are not supported in IE9 and earlier</h2>

<div class="box">
  <div class="top">top</div>
  <div class="caret"><i class="fa fa-caret-square-o-up"></i>
  </div>
  <div class="bottom">bottom</div>
</div>

<p>
  <button id="btnf">SPIN CLOCKWISE</button>
</p>
<p>
  <button id="btnb">SPIN COUTNER CLOCKWISE</button>
</p>

If you were using animations only for learning purpose, the details provided below should still be useful to you in terms of understanding how animations work, what are the limitations because of it etc.


Chris' answer touches upon the reason for your problem but I thought the question merited a bit more detailed explanation of two things - (1) Why the element doesn't hold the state as at the last keyframe even though animation-fill-mode: forwards setting is applied (2) Why the same keyframe couldn't be used for the reverse animation (when the class with the original animation was not removed). I also wanted to suggest a different alternate to the whole thing and hence the separate answer.

Why does the element not hold the state as at the last keyframe even though fill mode is set to forwards?

This is because you are removing the class that adds the animation as soon as animation pletes (inside the on('webkitAnimationEnd') event handler). Generally when animation-fill-mode is set to forwards, the UA uses the settings (or property-value pair) that are provided within last keyframe to maintain the state. But once the class is removed (and in-turn the animation settings), the UA does not keep track of (or know what) animations that were prior present on the element, their state and fill mode etc. Once animation is removed, the browser triggers a repaint and this will be performed based on classes that are present on the element as at the time of the repaint. Due to this, the element would snap back to its un-rotated state. You can read more about it in my answer here to a similar question (but not the same :)).

Why can't the same keyframe be used for the reverse animation (when the class which had the original animation was not removed)?

This again is because of how animations generally work. When any animation is added to an element, the UA maintains details about the animation's keyframes, its state etc as long as it is attached to the element. So, unless the class which added the forward (0° to 180°) animation is removed, the browser thinks that it has executed the animation to pletion (as default iteration count is just 1) and so even when a class with the reverse animation is added, it does nothing. The only way to make it restart the animation in reverse direction is by removing the class with the forward animation and then adding the class with the reverse animation. You can have a look at this answer also for related reading.

Because of the aforementioned reasons, the only way to achieve what you need with animations is to create two different animations (or keyframes) for the forward and reverse animations, set them under two different classes and keep changing the classes using JavaScript. This whole process bees tedious and is generally not necessary when all you need is a rotation from (0° to 180°) on the click of one button and back from (180° to 0°) on the other. This whole thing can be achieved using transitions and what makes this even better is the fact that transitions by default produce the reverse effect and so there is no need to code for two different states.

Further Reading:

  • What are the differences between Transitions and Animations

  • Choosing Transitions or Animations - When to use which?


If the need is to have continuous clockwise or counter-clockwise rotations with each button click (like in oMiKeY's answer) then I'd still remend using transition with a bit of JS like in the below snippet. Let's leave animations for more plex stuff (and in specific stuff that'd happen without any triggers).

(function() {
  var deg = 0;
  $('#btnb').on('click', function(e) {
    deg -= 180;
    return $('.box').css('transform', 'rotate(' + deg + 'deg)');
  });
  $('#btnf').on('click', function(e) {
    deg += 180;
    return $('.box').css('transform', 'rotate(' + deg + 'deg)');
  });
}.call(this));
.box {
  background: red;
  position: relative;
  width: 176px;
  height: 176px;
  margin: 40px auto;
  text-align: center;
  transition: transform 1s linear;  /* add this to enable transition */
}
.box .top {
  background: green;
  position: absolute;
  top: 0;
  width: 100%;
  height: 28px;
}
.box .bottom {
  background: purple;
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 28px;
}
.box .caret {
  color: white;
  font-size: 88px;
  position: absolute;
  top: 42px;
  left: 50px;
}
body {
  text-align: center;
  margin: 0 auto;
}
<link href="https://maxcdn.bootstrapcdn./font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>Keyframes are not supported in IE9 and earlier</h2>

<div class="box">
  <div class="top">top</div>
  <div class="caret"><i class="fa fa-caret-square-o-up"></i>
  </div>
  <div class="bottom">bottom</div>
</div>

<p>
  <button id="btnf">SPIN CLOCKWISE</button>
</p>
<p>
  <button id="btnb">SPIN COUTNER CLOCKWISE</button>
</p>

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

I fiddled with it for a while then decided you might need two separate rotation animations.

Check out my fiddle: http://codepen.io/anon/pen/jWBmZK

(function() {
  $('#btnb').on('click', function(e) {
    return $('.box')
    .addClass('spin-counter-clockwise')
    .toggleClass('upside-down');
  });
  $('#btnf').on('click', function(e) {
    return $('.box')
    .addClass('spin-clockwise')
    .toggleClass('upside-down');
  });
  $('.box').on('webkitAnimationEnd', function(e) {
    return $(e.target).removeClass('spin-counter-clockwise').removeClass('spin-clockwise');
  });
}.call(this));
.box {
  background: red;
  position: relative;
  width: 176px;
  height: 176px;
  margin: 40px auto;
  text-align: center;
}
.box .top {
  background: green;
  position: absolute;
  top: 0;
  width: 100%;
  height: 28px;
}
.box .bottom {
  background: purple;
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 28px;
}
.box .caret {
  color: white;
  font-size: 88px;
  position: absolute;
  top: 42px;
  left: 50px;
}
.upside-down {
    -moz-transform: rotate(180deg);
    -webkit-transform: rotate(180deg);
    -ms-transform: rotate(180deg);
    -o-transform: rotate(180deg);
    transform: rotate(180deg);
}
.spin-clockwise.upside-down {
  -moz-animation: spin 2s;
  -webkit-animation: spin 2s;
  -moz-animation-fill-mode: forwards;
  -webkit-animation-fill-mode: forwards;

}
.spin-counter-clockwise {
  -moz-animation: spin 2s reverse; 
  -webkit-animation: spin 2s reverse; 
  -moz-animation-fill-mode: forwards;
  -webkit-animation-fill-mode: forwards;

}
.spin-clockwise {
  -moz-animation: back-spin 2s;
  -webkit-animation: back-spin 2s;
  -moz-animation-fill-mode: forwards;
  -webkit-animation-fill-mode: forwards;
}
.spin-counter-clockwise.upside-down {
  -moz-animation: back-spin 2s reverse; 
  -webkit-animation: back-spin 2s reverse; 
  -moz-animation-fill-mode: forwards;
  -webkit-animation-fill-mode: forwards;
}
@keyframes spin {
  from {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  to {
    -webkit-transform: rotate(180deg);
    transform: rotate(180deg);
  }
}
@keyframes back-spin {
  from {
    -webkit-transform: rotate(180deg);
    transform: rotate(180deg);
  }
  to {
    -webkit-transform: rotate(360deg);
    transform: rotate(360deg);
  }
}
body {
  text-align: center;
  margin: 0 auto;
}
<link href="https://maxcdn.bootstrapcdn./font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>

<body>

  <h2>Keyframes are not supported in IE9 and earlier</h2>

  <div class="box">
    <div class="top">top</div>
    <div class="caret"><i class="fa fa-caret-square-o-up"></i>
    </div>
    <div class="bottom">bottom</div>
  </div>

  <p>
    <button id="btnf">SPIN CLOCKWISE</button>
  </p>
  <p>
    <button id="btnb">SPIN COUTNER CLOCKWISE</button>
  </p>
</body>

</html>

发布评论

评论列表(0)

  1. 暂无评论