CSS动画填充模式 - 我做错了什么?

3
我需要创建一个旋转动画。点击事件将元素旋转180°指向下方。另一个点击事件将同一元素旋转回0°指向上方。
我将animation-fill-mode设置为forwards以保留最后的关键帧状态。但它似乎没有起作用。所有视觉元素都会重置为默认状态。
有什么想法我可能做错了吗?
我的Codepen链接: http://codepen.io/simspace-dev/pen/RrpGmP 我的代码:

(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.com/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/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>


你使用动画有特定的原因吗?使用过渡效果比使用动画容易实现。 - Harry
1
谢谢,哈利。我会研究一下过渡效果。 - Chris Simeone
1
感谢Harry、Chris和oMiKeY提供的出色答案!如果最终我使用转换(听起来更好),我仍然很高兴尝试这种方法以获得学习经验。在此之前,我从未涉足CSS动画,现在我学到了很多。 - Chris Simeone
3个回答

3

TL;DR:(直接进入建议的解决方案)

如果您只需要在单击一个按钮时从0°旋转到180°,并在另一个按钮上从180°返回到0°,则建议使用过渡而不是动画。 过渡默认会产生相反的效果,因此无需编写两种不同的状态(这使其更好)。

(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.com/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/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>

如果您仅出于学习目的使用动画,则下面提供的细节仍然对您有用,因为它可以帮助您了解动画的工作原理、由此产生的限制等。
Chris的回答涉及了您问题的原因,但我认为这个问题值得更详细地解释两件事情 - (1) 为什么元素即使应用了“animation-fill-mode: forwards”设置也不能保持状态到最后一个关键帧(2) 当未删除具有原始动画的类时,为什么不能使用相同的关键帧进行反向动画。我还想提供一种不同的替代方案,因此有了单独的答案。
为什么元素即使设置了填充模式,也不能保持状态到最后一个关键帧?
这是因为您在动画完成后立即删除添加动画的类(在on('webkitAnimationEnd')事件处理程序中)。通常情况下,当animation-fill-mode设置为forwards时,UA使用最后一个关键帧中提供的设置(或属性值对)来维护状态。但是一旦删除了类(以及相应的动画设置),UA就不会跟踪(或知道)先前存在于元素上的动画、它们的状态和填充模式等。一旦动画被移除,浏览器触发重绘,并根据重新绘制时元素上存在的类执行此操作。由于这个原因,元素会回到未旋转的状态。您可以在我对类似问题的回答here中阅读更多相关信息(但并非完全相同)。 为什么不能对反向动画使用相同的关键帧(当没有删除原始动画的类时)? 这是因为动画的工作原理。当将任何动画添加到元素中时,UA会保持有关动画的关键帧、状态等详细信息,只要它附加到元素上。因此,除非删除了添加正向(0°至180°)动画的类,否则浏览器认为已经执行完整个动画(默认迭代次数为1),因此即使添加了反向动画的类,也不会发生任何事情。唯一使其以相反方向重新启动动画的方法是删除具有正向动画的类,然后添加具有反向动画的类。您还可以参考this answer进行相关阅读。
由于上述原因,实现动画所需的唯一方法是为正向和反向动画创建两个不同的动画(或关键帧),将它们设置在两个不同的类下,并使用JavaScript不断更改这些类。整个过程变得繁琐,通常并不必要,当你只需要在单击一个按钮时从(0°到180°)旋转,然后从(180°到0°)返回。这整个过程可以使用过渡来实现,更好的是,过渡默认产生反向效果,因此无需编写两种不同状态的代码。
进一步阅读:
- 过渡和动画之间的区别是什么? - 选择过渡还是动画 - 何时使用哪种?
如果需要每次按钮点击都进行连续的顺时针或逆时针旋转(就像oMiKeY的回答中一样),那么我仍然建议使用过渡效果并添加一些JS,就像下面的片段一样。让我们把动画留给更复杂的东西(以及在没有任何触发器的情况下发生的特定事情)。

(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.com/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/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>


1
很好的解释,Harry。谢谢你。 - Chris Simeone

2
所以根本问题是带有动画的类被移除了。
我无法使用相同的关键帧使其工作,但我创建了一个新的逆时针关键帧,并在按钮被点击时删除了相反的类。
更改:
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')
)

将spin-fill-mode类添加到box中。虽然您可能只需在动画类中保留fill-mode...

更新的CodePen链接:


太好了!谢谢。 - Chris Simeone

1
我调整了一段时间,然后决定您可能需要两个单独的旋转动画。
查看我的示例: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.com/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/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>


根据@Harry的评论,我决定使用变换而不是CSS动画。我创建了另一个Codepen示例,使用变换:http://codepen.io/simspace-dev/pen/XXMqwB。同时,我也更新了我先前的Codepen,使其按照上述说明进行工作:http://codepen.io/simspace-dev/pen/RrpGmP。 - Chris Simeone
我的无限旋转,而他的不会:P...开玩笑,这是一个有趣的问题,谢谢你提出好问题。 - omikes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接