背景
最近在公司做需求遇到一个很有趣很常见的需求也是实现一个类似手风琴的css效果,当鼠标移动上时,展开,扩展高度,当鼠标移开手,收缩,高度为0。
这里也分享给大家,我的实现思路和最终的解决方案。
如何实现手风琴效果
让我们从一个实际的例子开始。首先我们构建了一个简单的手风琴效果的菜单:
它的 HTML 代码非常简单:
我们先定义一个类名为 “accordion” 的 div 容器作为最外层的包裹,然后定义类名为 "accordion-title"的 div 容器作为标题容器,定义类名为"accordion-body"的div 容器作为内容容器
<div class="accordion">
<div class="accordion-title">悬停我!</div>
<div class="accordion-body">
<div>
<p>内容...</p>
</div>
</div>
</div>
我们希望实现的效果是当鼠标移上时,出现一个类似手风琴展开的效果,折叠时,类似手风琴收缩的效果,类似于“accordion-body”的高度平滑的从0过度到100。讲到这里,大家可能会立即想到,我们可以使用css的transition
属性来实现平滑过渡的效果。我最开始也是一样的想法。给accordion-body
加上transition: 500ms height ease;
应该就能实现。
css代码:
.accordion-body {
height: 0;
transition: 500ms height ease;
}
.accordion:hover .accordion-body {
height: auto;
}
❌ 但是经过实践,很明显,这种方法不起作用,CSS 不支持从 height: 0
过渡到 height: auto
。
那我们应该如何解决这个问题呢?
一种解决方案是我们可以将 height
属性设置为固定值,而不是 auto
。
虽然这样有效,但其实这不是一个特别好的方法:因为很多时候内容的具体高度是不固定的。
你可能会问?那我们还能使用纯 CSS 实现这个效果吗?
当然可以!我们可以使用 max-height
代替 height
呢?
.accordion-body {
max-height: 0;
transition: 500ms max-height ease;
}
.accordion:hover .accordion-body {
max-height: 200px;
}
由于我们给 max-height
定义了一个固定值,现在浏览器能够很好的地执行过渡动画了。
但是使用max-height
产生的问题是,因为我们给 max-height
定义了一个固定值,当实际内容的高度比容器的高度高时就会产生溢出。
jcode
如果你能够确保你的内容永远不会超过容器的最大高度……那么这种方法已经可以完全满足了!
不过我们要注意的是,当max-height
的值越高,它的过渡效果就会呈现的越奇怪。
你可能会问,那我们有更好的选择吗?
我们可不可以从一开始就避免使用任何固定的高度/最大高度呢?
CSS Grid 可以解决这个问题!
我们可以使用一个巧妙的技巧来实现。
我们需要做的是让我们的 grid-template-rows
从 0fr
过渡到 1fr
:这样,我们的网格项目就能从 0 过渡到它的 “内容” 高度。
/* 设置手风琴内容部分的样式 */
.accordion-body {
display: grid; /* 使用CSS网格布局 */
grid-template-rows: 0fr; /* 设置网格行模板,0fr表示行的高度为0,内容不可见 */
transition: 250ms grid-template-rows ease; /* 设置过渡效果,当行高度变化时,过渡时间为250毫秒,缓动函数为ease */
}
/* 当鼠标悬停在手风琴组件上时,改变手风琴内容部分的样式 */
.accordion:hover .accordion-body {
grid-template-rows: 1fr; /* 设置网格行模板,1fr表示行的高度将占用可用空间,内容变为可见 */
}
/* 设置手风琴内容部分直接子元素的样式 */
.accordion-body > div {
overflow: hidden; /* 设置内容溢出隐藏,这样当内容不可见时(高度为0),内容会被隐藏 */
}
我们需要注意的是,我们需要把.accordion-body
的内部 div 都设置为 overflow: hidden
,他的效果才会生效。