嘿,你可能尝试过在你的时间里使用过渡、变换和CSS中的动画来为一些HTML元素制作动画。我也试过,但当我在悬停时为某些东西制作动画时,出现了一个问题。
例如,你可以看看这个博客上的主题切换按钮(如果你在手机上,它就在顶部的菜单里):

当我第一次添加这个按钮和动画的时候,我犯了一个错误。你可以看到下面这个错误:

没有悬停突然结束动画。
我犯的错误很容易被忽视。如果你仔细看,你可以看到当我开始在它上面悬停时,动画就开始了,但只要我把鼠标移开,它就突然恢复到起点。这可能是一个小细节,大多数人都可以忽略这一点,但它从一开始就令我很恼火。想象一下,主题切换的代码是这样的:
.theme-switch:hover {
transition: transform 2s ease-in-out;
transform: rotate(360deg);
}
这一切都很好,它使用CSS旋转使太阳旋转,但一旦我把鼠标从太阳图标上移开,旋转就会突然重置。幸好,有一个简单的解决方案。我们需要把过渡规则放到一般的.theme-switch ,而不仅仅是:hover 规则:
.theme-switch {
transition: transform 2s ease-in-out;
}
.theme-switch:hover {
transform: rotate(360deg);
}
让我们看看我们移动过渡规则后得到了什么。
现在,它是丝般顺滑的!
如果我们试图将鼠标悬停并移开,它将使用我们的过渡规则平稳地恢复。再也没有恼人的突然重置的动画了,耶!
好了,如果你使用CSS过渡,这很有趣,也很容易,但如果你决定使用关键帧动画呢?继续阅读就知道了。
平稳地还原关键帧动画
首先,如果你正在研究恢复关键帧动画,你是否尝试过重新考虑你正在采取的方法?如果你做了,并且你意识到在这个世界上你没有机会使用过渡动画,那么请继续阅读。但如果你可以用一个简单的过渡来实现你的需求,请滚回本博文的第一部分。
好吧,你被一些关键帧卡住了,你想把它们还原,比方说,在悬停时。你如何实现这个目标?首先,只用CSS和HTML是很难做到的,但有一个办法。
例如,假设你有一个不断旋转的太阳图标,像这样:

旋转,旋转,旋转
它的代码看起来会是这样的:
.sun-icon {
animation: in 4s linear 0s infinite normal;
}
@keyframes in {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
这里我们使用@keyframes ,这个CSS规则定义了CSS动画的中间步骤。它与使用transition 不同的是,它让你对动画中的某些点的发生有更多的控制。我们希望受动画影响的元素从0度到360度,我们使用关键帧的from to 语法。我们也可以把0% 100% ,而不是from to 。它们是同义词,差不多。
最后,在.sun-icon 规则处,我们使用速记的animation 规则,在这里我们定义如下:
in- 动画的名称4s- 动画的持续时间linear- 动画的计时函数0s- 动画延迟(意味着我们的动画立即开始,因为延迟是0秒)。infinite- 我们的动画的迭代次数(动画将播放多少次)。normal- 动画的方向(也可以是 ,我们将在后面看到) 唷,这是很多次出现的动画词。reverse
现在我们有了这些,我们应该能够以某种方式恢复动画。我们该怎么做呢?你可能认为我们可以利用animation-direction ,把它放到reverse ,而不是normal 。好吧,如果我们这样做,我们就会有一个这样的跳跃动画。

太多的闪动
我们添加了这段代码来实现上述的内容:
.sun-icon:hover {
animation-direction: reverse;
}
但这显然是不需要的行为。我们还能怎样还原动画呢?
我们可以添加一个小技巧。我们可以将太阳图标包裹在一个容器中,当悬停在上面时,它将反向旋转。它将看起来像这样:

平滑,再次
这将是HTML部分:
<div class="sun-container">
<img class="sun-icon" src="sun.png" />
</div>
而这是CSS部分:
.sun-container {
width: 128px;
height: 128px;
animation: in 2s linear 0s infinite reverse;
animation-play-state: paused;
}
.sun-icon {
animation: in 4s linear 0s infinite normal;
}
.sun-container:hover {
animation-play-state: running;
}
@keyframes in {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
容器动画将运行2秒(比太阳图标动画快一倍,后者运行4秒)。另外,容器动画开始时是暂停的,但当我们将鼠标悬停在太阳容器元素上时,它将运行。
恢复关键帧动画的其他方法
还有其他的方法,但它永远不会像我向你展示的父元素和子元素都在旋转的技巧那样流畅。在《CSS技巧》一文中,有一种方法可以用JavaScript和玩弄关键帧来实现。
关键帧动画的主要问题是,没有办法跟踪它们的进展。例如,一旦动画开始,就没有浏览器API让你知道动画是否完成了46%。你有可能,setInterval ,让它在每10%的动画上启动,并在那里捣乱,但这是一个不可靠的解决方案。
让我解释一下为什么用setInterval (就像我上面分享的CSS技巧文章中的那个)的解决方案是不可靠的。想象一下,如果setInterval ,发射的时间有点晚,你推测你的进度是10%,但实际上你的进度是12%。如果你改变了动画,假设你仍然在10%的位置,如果你试图编辑它,你会得到动画的轻微跳动。这使我们回到了我们首先要解决的类似问题。
正如我们所看到的,我们不能利用animation-direction 规则,只是把它从normal 改为reverse ,反之亦然。每次你改变时,动画都会重置。
有一个潜在的解决方案可能是可行的,那就是使用多个动画。我们可以将一个关键帧动画分割成多个小的动画,并监听animationend 事件。如果你对这个解决方案感兴趣,可以考虑订阅新闻通讯,并在Twitter上关注我,我会在那里发布更多相关信息。
思考后再制作动画
我们已经经历了几个恢复动画的解决方案,但我们从来没有经历过最重要的一点--考虑你要做什么动画以及你是如何做的。在我们的例子中,使用关键帧来旋转一个元素可能没有意义。我们可以用CSS过渡来快速实现这个目的。我最后在我的博客上使用了这种方法。
我想说的是,也许有一个更直接的解决方案适合你。你可能会避免开发复杂的解决方案,避免为了解决一些问题而打破你的脑袋。如果你需要走更复杂的路线,那就去吧,没有什么可以阻止你。我在这里展示了几个选择和贯彻的方法。
但我希望这篇博文能对有这种问题的人有所帮助。通过并找出如何解决关键帧部分的方法很有趣。我很难过,在CSS规范中没有一个合适的解决方案,但我希望它在未来会出现。