CSS 实现子菜单逐渐弹出动画
本人属实 CSS 菜鸟,CSS 写的非常辣鸡,还请各位大佬轻喷
最近在使用 utools 中的一个插件时发现一个子菜单的弹出动画比较有意思
具体的说就是 hover 菜单图标时,子菜单从左到右依次显示,并且有缩放的效果。于是想着能不能不通过 js 方式而使用纯 css 来实现。最后通过不懈努力,终于实现了类似的效果,你可以通过这个 demo 体验codesandbox.io/s/html-css-…
虽然不是特别完美,但是在这个过程中也算是了解了 css 动画的一些基础。
transform 以及 transition
transform 以及 transition 是 css 中实现动画的一种手段。一般来说,transform 定义了动画的最终状态,而 transition 定义了状态改变的参数。例如:
p: hover{
transform: scale(1.5);
transition-delay: 150ms;
transition-duration: 100ms;
}
表示当 p 标签被 hover 时会放大 1.5 倍。而 transition-delay 以及 transition-duration 则分别表示这个状态改变延迟 150ms 执行,以及这个状态的改变会持续 100ms。当然 transform 以及 transition 对应的状态以及参数还有很多,这里就不一一列出。
回过头来看我们的需求:
- 子菜单逐渐显示:可以通过延时实现。例如每个子菜单的延时依次增大,则表现出一一显示的效果。
- 子菜单显示过程中伴随缩放: 即对应着 transform: scale 状态的改变
具体实现
首先完成基本框架:
<div id="wrap">
<div id="trigger"></div>
<div class="items">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
其中 wrap 时整个 demo 的容器,没有什么具体的作用。trigger 则表示我们用于触发子菜单显示的图标。而 items 则是正真的子菜单。每个 item 代表一个子菜单项目。
增加一些初始样式:
#wrap {
display: flex; /*flex 布局,使得 trigger 以及 items 显示在一行*/
justify-content: left;
margin-top: 100px;
margin-left: 100px;
}
#trigger{
width: 50px;
height: 50px;
border-radius: 50%;
background-color: rgb(44, 56,126);
box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.3);
box-shadow: rgba(0, 0, 0, 0.5);
}
.items {
display: flex; /*flex 布局,使得所有的 item 显示在一行*/
visibility: hidden; /* 子菜单初始应该 hidden */
}
.item {
width: 50px;
height: 50px;
border-radius: 50%;
transform: scale(0); /* 子菜单初始状态为 scale(0) 这样到 scale(1) 时就会出现缩放的效果 */
visibility: hidden;
box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.3);
margin-left: 20px;
}
随后为了 hover 子菜单依次出现,并出现缩放的效果,我们需要定义 hover 的样式:
#trigger:hover + .items .item:nth-of-type(1){
transform: scale(1);
visibility: visible;
transition-delay: 200ms; /* 显示的延迟依次增加,呈现一一显示的效果 */
transition-duration: 150ms;
}
#trigger:hover + .items .item:nth-of-type(2){
visibility: visible;
transition-delay: 300ms; /* 显示的延迟依次增加,呈现一一显示的效果 */
transform: scale(1);
transition-duration: 150ms;
}
#trigger:hover + .items .item:nth-of-type(3){
visibility: visible;
transition-delay: 400ms; /* 显示的延迟依次增加,呈现一一显示的效果 */
transform: scale(1);
transition-duration: 150ms;
}
这里主要使用 nth-of-type(i) 来选择不同的子菜单,并左到右依次增加 transition-delay 的时间,使得出现依次显示的效果。同样,但 trigger 失去 hover 时,我们需要一个从右到左依次增加的延迟时间。这样使得出现时为从左到右,消失时为从右到左:
.item:nth-of-type(1){
transition-delay: 300ms;
transition-duration: 150ms;
}
.item:nth-of-type(2){
transition-delay: 200ms;
transition-duration: 150ms;
}
.item:nth-of-type(3){
transition-delay: 150ms;
transition-duration: 150ms;
}
此时动画以及基本完成了,但是还有一个 bug。即一旦鼠标离开 trigger 去点击子菜单,trigger 就会失去 hover 的效果,子菜单就会消失。一个不能点击的子菜单明显不是我们需要的。于是我们还需要增加 items 的 hover 效果。这样就算鼠标离开 trigger,但只要在 items 之上子菜单也不会消失:
.items:hover .item{
transform: scale(1);
visibility: visible;
}
总结
实际上原理也非常简单:
- transform: scale() 实现缩放效果
- 结合 nth-of-type(i) 以及 transition-delay 实现依次出现的效果
最终的结果也有一些瑕疵,例如动画效果明显不够丝滑,多次短时间的 hover 容易卡顿等问题。如果各位大佬知道有更好的实现方式,请通过评论区带带我。