动画和过渡的组合不正常工作

7
我一直在尝试添加一些基本的CSS3动画。目标是在按钮的点击事件中切换类,并根据添加的类对div进行动画处理。这段代码在Firefox的第一次迭代中完美运行,但在其他浏览器(如Chrome)以及Firefox的下一次迭代中,变换只会在眨眼间完成。请帮助我找出问题所在。
代码段:

$('button').click(function() {
  $('div').toggleClass('clicked');
});
div {
  background-color: #ccc;
  height: 100px;
  width: 100px;
  transition-property: top, left;
  transition-duration: 1s;
  transition-timing-function: linear;
  position: relative;
  top: 0;
  left: 0;
}
.clicked {
  animation-name: clicked;
  animation-duration: 1s;
  animation-timing-function: linear;
  animation-fill-mode: forwards;
}
@keyframes clicked {
  0% {
    top: 0;
    left: 0;
  }
  100% {
    top: 100px;
    left: 100px;
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button type="button">Click Me!</button>
<div></div>

我还准备了一个示例链接


我在Ubuntu 15.04上使用Chrome,它对我来说似乎是有效的。如果我单击按钮一次,动画就会开始。如果我再次单击按钮,div就会回到其原始位置。然后,如果我再次单击按钮,动画就会重新开始,依此类推。 - Noble Mushtak
你真的需要动画吗?即使没有它,运动也可以实现线性运动。我之前在Chrome中看到过转换问题。您可以参考此线程获取一些相关信息-https://dev59.com/So3da4cB1Zd3GeqP37kC#31833533(我仅链接它是因为我们发现当动画被删除时,转换在Chrome中无法正常工作-这也在那里的问题中提到)。 - Harry
@Mushtak:我正在使用Windows 8.1,动画仅在Firefox中可用一次。从下一次迭代开始,动画不再像我所提到的那样流畅。对于Chrome / Opera,则在第一次迭代中出现问题。 - Rivnat
1
如果您可以避免动画,您可以在此处实现相同的效果(http://jsfiddle.net/hari_shanx/q5me2w4b/1/)。 - Harry
@Harry:谢谢你提供的参考链接和代码示例。这正是我所需要的。 - Rivnat
2个回答

7
这是Chrome的一个已知行为。Firefox似乎能够处理过渡时的动画移除,但Chrome则不能。我之前也在this thread中见过这种情况发生。
为什么在Chrome中使用过渡时移除动画不起作用?
虽然我无法提供100%可靠的解释,但我们可以根据这篇HTML5Rocks文章关于Chrome中加速渲染的内容这篇GPU加速合成在Chrome中的文章进行一定程度的解读。
似乎发生的是,元素因其具有显式位置属性而获得了自己的渲染层。当由于动画而使图层(或其部分)无效时,Chrome仅重新绘制受更改影响的图层。当您打开Chrome开发人员控制台并切换“显示绘制矩形”选项时,您会看到在动画发生时,Chrome仅绘制正在进行动画的实际元素。

然而,在动画开始和结束时,整个页面重绘正在发生,这会立即将元素放回其原始位置,从而覆盖过渡行为。

$('button').click(function(){
  $('div').toggleClass('clicked');
});
div{
  background-color: #ccc;
  height: 100px;
  width: 100px;
  transition-property: top, left;
  transition-duration: 1s;
  transition-timing-function: linear;
  position: relative;
  top: 0;
  left: 0;
}
.clicked{
  animation-name: clicked;
  animation-duration: 1s;
  animation-timing-function: linear;
  animation-fill-mode: forwards;
}
@keyframes clicked{
  0% {top: 0; left: 0;}
  100% {top: 100px; left: 100px;}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button type="button">Click Me!</button>
<div></div>


什么是解决方案?

由于您的移动实际上是从一个位置到另一个位置的线性移动,因此您可以在不需要任何动画的情况下实现它。我们所需要做的就是使用 translate 变换,并在切换类时将元素向所需像素数移动。由于元素通过另一个选择器分配了过渡,因此移位会以线性方式发生。当切换类时,元素会以线性方式移回其原始位置,因为元素上有过渡。

$('button').click(function() {
  $('div').toggleClass('clicked');
});
div {
  background-color: #ccc;
  height: 100px;
  width: 100px;
  transition-property: transform;
  transition-duration: 1s;
  transition-timing-function: linear;
  position: relative;
  top: 0;
  left: 0;
}
.clicked {
  transform: translate(100px, 100px);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<button type="button">Click Me!</button>
<div></div>


5
这是一种预期行为。你需要使用两个单独的动画或只使用转换。
过渡是一种动画,只是在两种不同状态之间执行的动画-即起始状态和结束状态。像一个抽屉菜单,起始状态可以是打开的,结束状态可以是关闭的,反之亦然。
如果您想执行一些与特定起始状态和结束状态无关的操作,或者您需要更细粒度地控制过渡中的关键帧,则必须使用动画。
以下是使用过渡对元素进行动画处理的方法:

$('button').click(function() {
  $('div').toggleClass('clicked');
});
div {
  background-color: #ccc;
  height: 100px;
  width: 100px;
  transition-property: top, left;
  transition-duration: 1s;
  transition-timing-function: linear;
  position: relative;
  top: 0;
  left: 0;
}
.clicked {
  top: 100px;
  left: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button type="button">Click Me!</button>
<div></div>


抱歉,老兄,在我回答之前没有看到你的答案弹出。对你的方法点赞,因为这也是实现它的正确方式。 - Harry
1
@halfzebra:谢谢你的回答。我希望我能接受两个答案! - Rivnat

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