问题
I'm trying to build a dropdown menu where first the dropdown slides down, then one-by-one menu items appear from the bottom. On hover, it looks good but I don't know how to play it smoothly on hover off. What I'm trying to do is hide the second sub-menu item first, then the first submenu item, then the dropdown slides up.
What I tried is to add the hide animation keyframes with a delay by default to the dropdown, and add the show animation on hover with 0 delay. But its not working properly as you can see.(and it will animate on page load too, which is not good for me)
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 (uses SCSS)
回答1:
As I had indicated in comments to the question, producing the reverse effect of an animation is always complex. It is not impossible but just that it requires extra coding (like extra keyframes) and tweaking of all related properties to achieve a perfect effect.
Using transition
is the best option if there is a need to achieve the reverse effect also. It is perfectly possible to produce the effect that you are after using transitions and it is all very similar to how you'd do it with animations. In the :hover
selector, apply the transition
setting such that :first-child
has a lower delay than the second (and subsequent children) so that it appears first and in the default selector apply the transition
setting such that the :first-child
has a higher delay than the rest.
Using transitions would also avoid the animation being shown on page load itself. This is because the transitions only happen when there is a state change from one to another.
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>
Note: In the above snippet, I've used the compiled CSS and removed all the -webkit-
prefixes to make it viewable on all browsers. If you want the SCSS code with the prefixes, you can find it here.
All that said, I still want to explain what was creating the problem with your animation
demo. You had done a lot of work correct and you were almost close to achieving what you want.
The way animations work is such that once the selector that is applying the animation is not applicable any longer, the animation gets removed (lost) and the element immediately snaps back to its default or original state. Even though another animation was being applied on hover out, the properties specified within the reverse animation's keyframe settings do not get applied until their delay time is elapsed. This means that the ul
and a
snap back to being invisible (either through opacity
or visibility
) setting, become visible again when the animation starts (through from
keyframe) and then animate to the end state (again invisible through the to
keyframe).
The solution to this problem is to set animation-fill-mode: backwards
to the ul
and a
(unhovered state). This setting would mean that the element takes the properties specified in the from
keyframes even during the delay period.
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>
Note: Same as with first snippet, the above one also uses plain CSS with no prefixes. SCSS version with prefixes is available here.
But as mentioned earlier, this wouldn't prevent the animation from being visible on page load. The only way to stop that is by using JavaScript.
来源:https://stackoverflow.com/questions/35196848/how-to-play-reverse-keyframe-animation-for-hover-off