使用纯CSS给不定高度的容器添加过渡效果-flex与grid还在为高度过渡效果苦恼吗?快来瞧瞧吧,今天叫你两种新方式,保
写在开头
哈喽,各位UU早上好呀!😋
最近,小编忙于整理一些前端的知识点,不停感叹前端的体系真是越来越庞大了,太多也太杂,有点头大。😵
而且呢,俺发现在这些体系下,还存在很多令人抓狂的词汇缩写,如下:
这些还只是很小很小一部分,随手想到就写,还有太多太多。😐
真是太难了,这年头,不卷不行!
算了,回到正题!这次要分享的是关于高度过渡效果的内容,具体效果如下,请诸君按需食用。
问题
今天,在Coding中寻思着给新做的下拉菜单增添一点"魔法"🌈——也就是给它展开和收起加上个高度过渡效果,让操作体验更好些。
Em...😐这不是手到擒来的事嘛,开整:
<!DOCTYPE html>
<html>
<head>
<style>
body {
padding: 20vh;
margin: 0;
height: 100vh;
display: flex;
align-items: center;
flex-direction: column;
}
.dropdown {
height: 0;
transition: height 2s linear;
overflow: hidden;
color: #fff;
background-color: #5468ff;
}
.dropdown > div {
height: 34px;
line-height: 34px;
padding: 0 10px;
}
.container:has(.btn:hover) .dropdown {
height: auto;
}
</style>
</head>
<body>
<div class="container">
<button class="btn">Hover me!</button>
<div class="dropdown">
<div>苹果~</div>
<div>香蕉~</div>
<div>水蜜桃~</div>
<div>橙子~</div>
<div>葡萄~</div>
</div>
</div>
</body>
</html>
效果:

呃...效果有点差强人意,根本没用!
天真了🙊,以为让高度(height
)从0到auto过渡就能搞定!结果却不行。❌
在
CSS
中,如果我们尝试将height
从0过渡到auto ,那么它将不起作用,会发生的情况是元素将直接捕捉到height:auto
状态而不进行过渡。
分析与方案
其实这个问题背后真正的原因是:回流。
回流与重绘相信前端小伙伴都有所了解的,懂得都懂啦。😋
-
回流:回流也称重排,当DOM进行一些位置和大小(几何信息)的变化时,浏览器需要重新计算元素的信息,再将其重新放置或绘制在正确的位置上,这个过程就叫做回流。
-
重绘:当一个元素外观发生变化,但是没有改变元素的布局,只需要重新把元素的外观绘制出来,这个过程就叫做重绘。
有时候,咱们可能只是轻轻一挥手,改了某一个元素的样式,却意外地触发了浏览器的大动作——它得重新绘制整个页面的大工程,这个过程可是相当耗资源的!
呃...反正呢,要知道回流是一项成本非常高的操作。
而现在如果浏览器能处理从0转换为auto的过程,则浏览器必须对动画的每一步执行重排以确定其他元素的位置,记住,是每一帧都要进行重排哦!但由于缺少起始值和结束值,这一过程无法被优化,从而可能拖累性能。
所以,结论:height
从0过渡到auto的过程无法被浏览器所处理。🙈
不过,山重水复疑无路,柳暗花明又一村。东路不通,就走西路呗,现在已经有一些其他方案来解决这个问题:
-
最大高度猜测大法:我们可以给容器设定一个足够大的最大高度,然后过渡到0。但缺点是,如果预估的高度过大,过渡动画就会变得漫长,影响用户体验。
-
缩放(scale)大法:从scale(0)动画过渡到scale(1),虽然这个方法不会引起回流,但内容的扭曲可能会让动画看起来不太自然。
-
JS 魔法大法:通过JavaScript动态获取内容高度,这无疑是最佳方案,但需要额外的时间和代码来实现。
😪 就这?
当然不是,这些都有一定的弊端!
下面,来介绍两种更为优雅的方法,纯CSS噢😉,它们可以在不损害动画美观和浏览器性能的前提下,实现从0到auto的高度过渡。
方案一:flex
flex 布局相信也都知道,在该布局下,其最大高度(max-height
)的行为有所不同。在弹性布局中,最大高度的值将始终是弹性布局的高度,这是一个结论❗
但是,咱们需要一个额外的 div
才能完成,Em......。🙊
先直接来看吧,如下:
<!DOCTYPE html>
<html>
<head>
<style>
body {
padding: 20vh;
margin: 0;
height: 100vh;
display: flex;
align-items: center;
flex-direction: column;
}
.dropdown {
transition: all 0.3s ease;
overflow: hidden;
color: #fff;
background-color: #5468ff;
/* 通过max-height来过渡 */
max-height: 0;
}
.dropdown > div {
height: 34px;
line-height: 34px;
padding: 0 10px;
}
.container:has(.btn:hover) .dropdown {
/* 通过max-height来过渡 */
max-height: 100%;
}
/* 使用flex布局 */
.dropdown-box {
display: flex;
}
</style>
</head>
<body>
<div class="container">
<button class="btn">Hover me!</button>
<div class="dropdown-box">
<!-- 额外必须的div -->
<div>
<div class="dropdown">
<div>苹果~</div>
<div>香蕉~</div>
<div>水蜜桃~</div>
<div>橙子~</div>
<div>葡萄~</div>
</div>
</div>
</div>
</div>
</body>
</html>
效果:
还可以吧,效果上看是能满足的。唯一缺陷就是多一个额外 div
的存在,有点会膈应。😶
方案二:grid(fr)
方案二需要使用到网格布局——grid,而且它还用上了表达比例关系的 fr 单位。
实现如下:
<!DOCTYPE html>
<html>
<head>
<style>
body {
padding: 20vh;
margin: 0;
height: 100vh;
display: flex;
align-items: center;
flex-direction: column;
}
.dropdown {
overflow: hidden;
color: #fff;
background-color: #5468ff;
}
.dropdown > div {
height: 34px;
line-height: 34px;
padding: 0 10px;
}
.container:has(.btn:hover) .dropdown-box {
grid-template-rows: 1fr;
}
.dropdown-box {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.5s ease-out;
}
</style>
</head>
<body>
<div class="container">
<button class="btn">Hover me!</button>
<div class="dropdown-box">
<div class="dropdown">
<div>苹果~</div>
<div>香蕉~</div>
<div>水蜜桃~</div>
<div>橙子~</div>
<div>葡萄~</div>
</div>
</div>
</div>
</body>
</html>
效果:
又有了一定的改进吧,也不需要额外的 div
元素的协同了,这就挺好。😃
至此,本篇文章就写完啦,撒花撒花。
转载自:https://juejin.cn/post/7403426280941666304