通常,你想要通过css transition
来过渡属性,然后使用了下面代码transition: height 0.5s linear
然后又使用了 height: auto
来自适应高度height: 0
-> height: auto
然后发现动画就像没发生一样,直接就在0和auto两个状态之间直接切换
你可能会认为出现了什么bug,或者代码写错了,其实不然
你可能发生下图所示的情况
在这篇文章中,都使用高度进行说明,这张图里的宽度与高度是一个道理
这个问题目前还不能简单通过css完全解决
下面是我分享的4个解决方案
本文章部分资料以及Code Pen代码引用于 css-tricks.com, 里面有很多关于css的技巧,值得学习
这是浏览器bug?
查阅Mozilla Developer Network docs, auto 属性值被排除在transition规范之外。 这样做的原因是因为,如果transition一个元素的高度到auto,浏览器将会进行页面重排(reflow),重排页面会计算其他元素的位置,并且在每一个动画帧都会进行这样的操作,这意味着将花费巨大的开销。
虽然设置height: auto达到不了我们想要的效果,但是可以通过其他方法实现,下面介绍4种方式
- 使用max-height
- 使用transform: scaleY()
- 使用Javascript
- 使用Flex容器
解决方案 1: 使用 max-height
这个方法可能是最容易搜索到的方法,但是不太理想,不过在一些情况下,还是值得使用
具体方法大概如下:
- 设置 transition: max-height 0.3s ease-out
- max-height: 0
- 过渡到一个该容器能达到的最大值,比如 1000px
- max-height: 1000px
这里的max-height必须保证大于容器auto的height,不然就会出现内容显示不全的情况
Code Pen演示
在以下的code pen演示中都使用的SCSS,如果需查看CSS,请点击下面的View Compiled按钮
主要注意.section这部分
1 | .section { |
缺点
可能你也发现了,这里的max-height也是硬编码,大多数情况下也不好确定容器的最大内容高度。
其次,设置的transition的时间和变换函数和实际展示出来的并不一致
打个比方
- 内容的实际最高宽度为100px
- 你设置了:
- max-height: 0 -> max-height: 1000px
- transition: max-height 10s linear;
- 那么这里的10s的transition显示在浏览器上就只有1s,因为1s后max-height: 100px已经达到容器最大高度,后面的max-height的变化不会体现出来
解决方案 2: transform: scaleY()
该方法不会触发页面重排(reflow)
设置方法很简单
设置 transform: scaleY(0) 到 transform: scaleY(1) 即可完成高度的缩放
该方法不会触发重排,所以元素位置不会改变,同时元素的内容会产生挤压的形变效果
Code Pen 演示
1 | .section { |
缺点
该方法普遍不适用
产生的形变效果大部分时候不是我们想要的,也不够没关
不会产生页面重排,折叠后的位置留空,大部分时候不是我们想要的情况
解决方案 3: Javascript(推荐)
该方法使用js获取到内容的最大高度,然后就能使用固定的高度进行transition了
基本思路是使用,获取到元素的高度
1 | element.scrollHeight |
然后设置
1 | element.style.height = scrollHeight |
点击后设置
1 | element.style.height = 0 |
这样就能实现css的transition效果,并且移除了硬编码,更加灵活,适用于任意高度的容器
Code Pen 演示
要点在这两部分
1 | var sectionHeight = element.scrollHeight; // 获取到内容的高度 |
关于scrollHeight
Element.scrollHeight 这个只读属性是一个元素内容高度的度量,包括由于溢出导致的视图中不可见内容。
我这里放个图大家就知道了
这里使用到了requestAnimationFrame()
,关于这个我会在之后的博文讲解。
下面是我写的另外一种做法,比这个要直观一点
1 | document.querySelectorAll('.dropdown-nav__title').forEach((item, index) => { |
缺点
同样的,这里没有避免掉页面重排的问题
不过在某些特定的情况下,可以通过使用绝对定位来避免页面重排,比如导航栏的下拉菜单如果需要设置动画,就不需要重排页面。
解决方案 4: FlexBox (额外方式)
之所以称之为额外方式,是因为从技术上来说该方式没有达到预期的效果,不过却是另外一种不错的方法
如果你还不够了解flexbox 和 flex-grow两个属性,推荐阅读
A Complete Guide to FlexBox (英文)
这里的话主要就是用到的flex这个属性了(实质上为flex-grow)
Code Pen 演示
1 | .section { |
请注意上面的
1 | overflow: hidden; |
这句话是关键,可替换为
1 | min-height: 0; |
因为默认情况下,元素不会缩短至小于内容框尺寸,若想改变这一状况,请设置元素的min-width
与 min-height
属性。同样的设置overflow: hidden
也可达到相同效果
这又是另一种效果,所以说是一种额外的解决方法。
不过这种方法利用了flexbox,更加灵活
总结
这四种方式各有优缺点,没有优劣,不过个人还是比较喜欢用js的方式,不过至于到底该用哪个,根据你的需求和情况而定。