如何为悬停播放反向关键帧动画?
How to play reverse keyframe animation for hover off?
我正在尝试构建一个下拉菜单,首先下拉菜单向下滑动,然后一个接一个的菜单项从底部出现。悬停时,它看起来不错,但我不知道如何在悬停时流畅地播放它。我要做的是先隐藏第二个子菜单项,然后隐藏第一个子菜单项,然后下拉菜单向上滑动。
我尝试的是将默认延迟的隐藏动画关键帧添加到下拉列表中,并在悬停时添加显示动画,延迟为 0。但正如您所见,它无法正常工作。(它也会在页面加载时设置动画,这对我来说不太好)
ul li ul {
-webkit-animation: hideDropdown 1s;
-webkit-animation-delay: 0.5s;
}
ul li:hover ul {
-webkit-animation: showDropdown 1s forwards;
-webkit-animation-delay: 0s;
}
Fiddle Demo(使用 SCSS)
正如我在问题评论中指出的那样,制作动画的反向效果总是很复杂。这并非不可能,只是需要额外的编码(如额外的关键帧)和调整所有相关属性才能达到完美的效果。
如果还需要达到相反的效果,使用transition
是最好的选择。使用过渡后完全有可能产生您现在的效果,这与您使用动画的方式非常相似。在 :hover
选择器中,应用 transition
设置,使 :first-child
的延迟比第二个(和后续子级)低,以便它首先出现,并在默认选择器中应用 transition
设置使得 :first-child
的延迟比其他的高。
使用过渡也可以避免在页面加载时显示动画。这是因为只有当状态从一个状态更改为另一个状态时才会发生转换。
body {
background: #f1f1f1;
}
ul {
height: 100px;
position: relative;
background: #fff;
}
ul li {
display: block;
float: left;
margin: 0;
list-style-type: none;
padding: 0;
}
ul li a {
display: block;
height: 100px;
line-height: 100px;
padding: 0 20px;
}
ul li ul {
position: absolute;
left: 0px;
right: 0px;
top: 100px;
height: 0px;
opacity: 0;
visibility: hidden;
transition: all 1s 0.5s;
}
ul li ul li {
position: absolute;
left: 50px;
bottom: 0px;
width: 200px;
overflow: hidden;
height: 100px;
}
ul li ul li a {
display: block;
height: 100px;
background: red;
padding: 0 20px;
position: absolute;
opacity: 0;
transform: translate(0px, 100px);
transition: all 1s 0.2s;
}
ul li ul li:first-child a {
transition-delay: 0.4s;
}
ul li ul li:last-child {
left: 250px;
}
ul li ul li:last-child a {
transition-delay: 0.2s;
}
ul li:hover ul {
height: 100px;
opacity: 1;
visibility: visible;
transition: all 1s 0s;
}
ul li:hover ul li a {
opacity: 1;
transform: translate(0px, 0px);
transition: all 1s 0.2s;
}
ul li:hover ul li:last-child a {
transition-delay: 0.4s;
}
<ul>
<li>
<a href="#">Dropdown</a>
<ul>
<li><a href="">First</a></li>
<li><a href="">second</a></li>
</ul>
</li>
<li><a href="">Not dropdown</a></li>
</ul>
注意: 在上面的代码片段中,我使用了编译后的 CSS 并删除了所有 -webkit-
前缀以使其在所有浏览器上都可见.如果你想要带前缀的 SCSS 代码,你可以找到它 here.
综上所述,我仍然想解释是什么导致了您的 animation
演示出现问题。您已经完成了很多正确的工作,并且几乎接近于实现您的目标。
动画的工作方式是,一旦应用动画的选择器不再适用,动画就会被移除(丢失)并且元素会立即恢复到其默认或原始状态。即使在悬停时应用了另一个动画,在反向动画的关键帧设置中指定的属性也不会应用 直到它们的延迟时间结束 .这意味着 ul
和 a
会恢复到不可见状态(通过 opacity
或 visibility
)设置,当动画开始时(通过 from
关键帧),然后动画到结束状态(同样通过 to
关键帧不可见)。
这个问题的解决方法是将animation-fill-mode: backwards
设置为ul
和a
(未悬停状态)。此设置意味着该元素即使在延迟期间也会采用 from
关键帧中指定的属性。
body {
background: #f1f1f1;
}
ul {
height: 100px;
position: relative;
background: #fff;
}
ul li {
display: block;
float: left;
margin: 0;
list-style-type: none;
padding: 0;
}
ul li a {
display: block;
height: 100px;
line-height: 100px;
padding: 0 20px;
}
ul li ul {
position: absolute;
left: 0px;
right: 0px;
top: 100px;
height: 0px;
visibility: hidden;
animation: hideDropdown 1s;
animation-delay: 0.5s;
animation-fill-mode: backwards;
}
ul li ul li {
position: absolute;
left: 50px;
bottom: 0px;
width: 200px;
overflow: hidden;
height: 100px;
}
ul li ul li a {
display: block;
height: 100px;
background: red;
padding: 0 20px;
position: absolute;
opacity: 0;
transform: translate(0px, 100px);
animation: hideDropdownItem 1s 0.2s;
animation-fill-mode: backwards;
}
ul li ul li:first-child a {
animation-delay: 0.4s;
}
ul li ul li:last-child {
left: 250px;
}
ul li ul li:last-child a {
animation-delay: 0.2s;
}
ul li:hover ul {
animation: showDropdown 1s forwards;
animation-delay: 0s;
}
ul li:hover ul li a {
animation: showDropdownItem 1s forwards 0.2s;
}
ul li:hover ul li:last-child a {
animation-delay: 0.4s;
}
@keyframes showDropdown {
from {
opacity: 0;
height: 0;
visibility: hidden;
}
to {
height: 100px;
opacity: 1;
visibility: visible;
}
}
@keyframes hideDropdown {
from {
height: 100px;
opacity: 1;
visibility: visible;
}
to {
opacity: 0;
height: 0;
visibility: hidden;
}
}
@keyframes showDropdownItem {
from {
opacity: 0;
transform: translate(0px, 100px);
}
to {
opacity: 1;
transform: translate(0px, 0px);
}
}
@keyframes hideDropdownItem {
from {
opacity: 1;
transform: translate(0px, 0px);
}
to {
opacity: 0;
transform: translate(0px, 100px);
}
}
<ul>
<li>
<a href="#">Dropdown</a>
<ul>
<li><a href="">First</a>
</li>
<li><a href="">second</a>
</li>
</ul>
</li>
<li><a href="">Not dropdown</a>
</li>
</ul>
注意: 与第一个片段相同,上面的片段也使用没有前缀的普通 CSS。带有前缀的 SCSS 版本可用 here.
但是如前所述,这不会阻止动画在页面加载时可见。停止这种情况的唯一方法是使用 JavaScript.
有时使用animation-direction: alternate;
反转当前动画会更容易。
我正在尝试构建一个下拉菜单,首先下拉菜单向下滑动,然后一个接一个的菜单项从底部出现。悬停时,它看起来不错,但我不知道如何在悬停时流畅地播放它。我要做的是先隐藏第二个子菜单项,然后隐藏第一个子菜单项,然后下拉菜单向上滑动。
我尝试的是将默认延迟的隐藏动画关键帧添加到下拉列表中,并在悬停时添加显示动画,延迟为 0。但正如您所见,它无法正常工作。(它也会在页面加载时设置动画,这对我来说不太好)
ul li ul {
-webkit-animation: hideDropdown 1s;
-webkit-animation-delay: 0.5s;
}
ul li:hover ul {
-webkit-animation: showDropdown 1s forwards;
-webkit-animation-delay: 0s;
}
Fiddle Demo(使用 SCSS)
正如我在问题评论中指出的那样,制作动画的反向效果总是很复杂。这并非不可能,只是需要额外的编码(如额外的关键帧)和调整所有相关属性才能达到完美的效果。
如果还需要达到相反的效果,使用transition
是最好的选择。使用过渡后完全有可能产生您现在的效果,这与您使用动画的方式非常相似。在 :hover
选择器中,应用 transition
设置,使 :first-child
的延迟比第二个(和后续子级)低,以便它首先出现,并在默认选择器中应用 transition
设置使得 :first-child
的延迟比其他的高。
使用过渡也可以避免在页面加载时显示动画。这是因为只有当状态从一个状态更改为另一个状态时才会发生转换。
body {
background: #f1f1f1;
}
ul {
height: 100px;
position: relative;
background: #fff;
}
ul li {
display: block;
float: left;
margin: 0;
list-style-type: none;
padding: 0;
}
ul li a {
display: block;
height: 100px;
line-height: 100px;
padding: 0 20px;
}
ul li ul {
position: absolute;
left: 0px;
right: 0px;
top: 100px;
height: 0px;
opacity: 0;
visibility: hidden;
transition: all 1s 0.5s;
}
ul li ul li {
position: absolute;
left: 50px;
bottom: 0px;
width: 200px;
overflow: hidden;
height: 100px;
}
ul li ul li a {
display: block;
height: 100px;
background: red;
padding: 0 20px;
position: absolute;
opacity: 0;
transform: translate(0px, 100px);
transition: all 1s 0.2s;
}
ul li ul li:first-child a {
transition-delay: 0.4s;
}
ul li ul li:last-child {
left: 250px;
}
ul li ul li:last-child a {
transition-delay: 0.2s;
}
ul li:hover ul {
height: 100px;
opacity: 1;
visibility: visible;
transition: all 1s 0s;
}
ul li:hover ul li a {
opacity: 1;
transform: translate(0px, 0px);
transition: all 1s 0.2s;
}
ul li:hover ul li:last-child a {
transition-delay: 0.4s;
}
<ul>
<li>
<a href="#">Dropdown</a>
<ul>
<li><a href="">First</a></li>
<li><a href="">second</a></li>
</ul>
</li>
<li><a href="">Not dropdown</a></li>
</ul>
注意: 在上面的代码片段中,我使用了编译后的 CSS 并删除了所有 -webkit-
前缀以使其在所有浏览器上都可见.如果你想要带前缀的 SCSS 代码,你可以找到它 here.
综上所述,我仍然想解释是什么导致了您的 animation
演示出现问题。您已经完成了很多正确的工作,并且几乎接近于实现您的目标。
动画的工作方式是,一旦应用动画的选择器不再适用,动画就会被移除(丢失)并且元素会立即恢复到其默认或原始状态。即使在悬停时应用了另一个动画,在反向动画的关键帧设置中指定的属性也不会应用 直到它们的延迟时间结束 .这意味着 ul
和 a
会恢复到不可见状态(通过 opacity
或 visibility
)设置,当动画开始时(通过 from
关键帧),然后动画到结束状态(同样通过 to
关键帧不可见)。
这个问题的解决方法是将animation-fill-mode: backwards
设置为ul
和a
(未悬停状态)。此设置意味着该元素即使在延迟期间也会采用 from
关键帧中指定的属性。
body {
background: #f1f1f1;
}
ul {
height: 100px;
position: relative;
background: #fff;
}
ul li {
display: block;
float: left;
margin: 0;
list-style-type: none;
padding: 0;
}
ul li a {
display: block;
height: 100px;
line-height: 100px;
padding: 0 20px;
}
ul li ul {
position: absolute;
left: 0px;
right: 0px;
top: 100px;
height: 0px;
visibility: hidden;
animation: hideDropdown 1s;
animation-delay: 0.5s;
animation-fill-mode: backwards;
}
ul li ul li {
position: absolute;
left: 50px;
bottom: 0px;
width: 200px;
overflow: hidden;
height: 100px;
}
ul li ul li a {
display: block;
height: 100px;
background: red;
padding: 0 20px;
position: absolute;
opacity: 0;
transform: translate(0px, 100px);
animation: hideDropdownItem 1s 0.2s;
animation-fill-mode: backwards;
}
ul li ul li:first-child a {
animation-delay: 0.4s;
}
ul li ul li:last-child {
left: 250px;
}
ul li ul li:last-child a {
animation-delay: 0.2s;
}
ul li:hover ul {
animation: showDropdown 1s forwards;
animation-delay: 0s;
}
ul li:hover ul li a {
animation: showDropdownItem 1s forwards 0.2s;
}
ul li:hover ul li:last-child a {
animation-delay: 0.4s;
}
@keyframes showDropdown {
from {
opacity: 0;
height: 0;
visibility: hidden;
}
to {
height: 100px;
opacity: 1;
visibility: visible;
}
}
@keyframes hideDropdown {
from {
height: 100px;
opacity: 1;
visibility: visible;
}
to {
opacity: 0;
height: 0;
visibility: hidden;
}
}
@keyframes showDropdownItem {
from {
opacity: 0;
transform: translate(0px, 100px);
}
to {
opacity: 1;
transform: translate(0px, 0px);
}
}
@keyframes hideDropdownItem {
from {
opacity: 1;
transform: translate(0px, 0px);
}
to {
opacity: 0;
transform: translate(0px, 100px);
}
}
<ul>
<li>
<a href="#">Dropdown</a>
<ul>
<li><a href="">First</a>
</li>
<li><a href="">second</a>
</li>
</ul>
</li>
<li><a href="">Not dropdown</a>
</li>
</ul>
注意: 与第一个片段相同,上面的片段也使用没有前缀的普通 CSS。带有前缀的 SCSS 版本可用 here.
但是如前所述,这不会阻止动画在页面加载时可见。停止这种情况的唯一方法是使用 JavaScript.
有时使用animation-direction: alternate;
反转当前动画会更容易。