前言
前端项目总有很多点击打开/关闭某个元素的需求,为了给用户更好的体验,我们在打开或收起一个元素的时候,希望它能有一个过渡动画,而不是直接出现或消失。而当一个元素的高度不固定时使用 transition + height: auto, 是无法实现动画效果的。以下我总结了五种实现方法,同时对比了它们的优缺点以及适用范围。
一、使用 interpolate-size
interpolate-size 是 CSS 中的一个新属性,它可以让元素尺寸相关的属性在过渡过程中进行插值计算。通过在需要过度元素的祖先元素上设置 interpolate-size: allow-keywords;,可以让元素的高度在过渡过程中进行插值计算,从而实现过渡动画。
css 代码
:root {
interpolate-size: allow-keywords; /* 重点 加上该属性后 使用 transition + height: auto 就可以有过度动画了 */
}
.interpolate-size-container {
height: 0px;
overflow: hidden;
transition: height 0.3s linear;
}
.interpolate-size-container.open {
height: auto;
}
html 代码
<button id="toggle-button">切换</button>
<div
id="target"
class="interpolate-size-container"
style="background-color: aquamarine">
<p>interpolate-size: allow-keywords</p>
<p>2 不固定</p>
<p>3 未设置高度</p>
</div>
js 代码
document.getElementById('toggle-button').addEventListener('click', function () {
const target = document.getElementById('target')
target.classList.toggle('open')
})
实现效果
二、使用 calc-size
calc-size 是 CSS 中的一个新函数,他可以使得 auto、max-content、min-content 等非确定数值的值可以拥有过度效果。
css 代码
.calc-size-container {
height: 0px;
overflow: hidden;
transition: height 0.3s linear;
}
.calc-size-container.open {
height: calc-size(max-content, size);
}
html 代码
<button id="toggle-button">切换</button>
<div
id="target"
class="calc-size-container"
style="background-color: aquamarine">
<p>calc-size</p>
<p>2 不固定</p>
<p>3 未设置高度</p>
</div>
js 代码
document.getElementById('toggle-button').addEventListener('click', function () {
const target = document.getElementById('target')
target.classList.toggle('open')
})
实现效果
三、使用 grid 布局
这里是利用 grid 的 fr 单位,由于 fr 前是数值所以可以被 css 计算,从而实现过渡动画。 注意:grid 内需要有一个元素,因为 fr 是设置到子元素上的,并且这个元素的最小高度需要设置为 0,否则会被内容撑开。
css 代码
.grid-example-container {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s linear;
overflow: hidden;
}
.grid-example-container > div {
min-height: 0px;
}
.grid-example-container.open {
grid-template-rows: 1fr;
}
html 代码
<button id="toggle-button">切换</button>
<div
id="target"
class="grid-example-container"
style="background-color: aquamarine">
<div>
<p>grid</p>
<p>2 不固定</p>
<p>3 未设置高度</p>
</div>
</div>
js 代码
document.getElementById('toggle-button').addEventListener('click', function () {
const target = document.getElementById('target')
target.classList.toggle('open')
})
实现效果
四、使用 max-height
设置 max-height 为固定的数值,这个数值比需要超过元素的实际高度;这里就有一个小问题,如果元素的高度完全未知,那么 max-height 的值需要设置得特别大,否则展开时将不能看到元素的全部内容;但是实际过度的是 max-height 的值,所以在过度时当 max-height 超过元素实际高度时后的时间过度动画将不可见了,由此会出现一个问题,展开时实际过度时间比设置的过度时间短,收起时会 delay 一下然后再执行过度,并且过度的速度将会比较快。
css 代码
.max-height-container {
max-height: 0px;
transition: max-height 0.3s linear;
overflow: hidden;
}
.max-height-container.open {
max-height: 400px;
}
html 代码
<button id="toggle-button">切换</button>
<div
id="target"
class="max-height-container"
style="background-color: aquamarine">
<p>max-height</p>
<p>2 不固定</p>
<p>3 未设置高度</p>
</div>
js 代码
document.getElementById('toggle-button').addEventListener('click', function () {
const target = document.getElementById('target')
target.classList.toggle('open')
})
实现效果
五、使用 js 计算高度
利用 js 读取元素的实际高度,给元素设置高度的数值,即可实现动画效果;这种方式实现起来相对比较复杂,但是灵活性强
css 代码
.js-container {
transition: height 0.3s linear;
overflow: hidden;
}
html 代码
<button id="toggle-button">切换</button>
<div
id="target"
class="js-container"
style="background-color: aquamarine">
<p>js 计算</p>
<p>2 不固定</p>
<p>3 未设置高度</p>
</div>
js 代码
document.getElementById('toggle-button').addEventListener('click', function () {
const target = document.getElementById('target')
const { height } = target.getBoundingClientRect()
if (height === 0) {
const factHeight = target.scrollHeight
target.style.height = '0px'
target.getBoundingClientRect() // 强制css从新计算,让设置的height=0px生效
target.style.height = `${factHeight}px`
setTimeout(() => {
target.style.height = '' // 动画完成后,将高度取消,以适应其高度变化
}, 300)
} else {
target.style.height = `${height}px`
target.getBoundingClientRect() // 强制css从新计算,让设置的height=${height}px生效
target.style.height = '0px'
}
})
实现效果
总结
| 方法 | 优点 | 缺点 | 使用范围 |
|---|---|---|---|
| interpolate-size | 简单易用,css 支持的属性,性能好 | 仅比较新的浏览器支持 | chrome:129+,edge:129+,opera:115+ |
| calc-size | 简单易用,css 支持的属性,性能好 | 仅比较新的浏览器支持 | chrome:129+,edge:129+,opera:115+ |
| grid | 简单易用,兼容性相对较好 | 需要多嵌套一层 dom | 无需兼容低版本浏览器 |
| max-height | 简单易用,兼容所有浏览器 | 用户体验不好,看起来像有点卡顿 | 元素实际高相差不大,比较可控时 |
| js 计算 | 兼容所有浏览器,可控性好 | 实现复杂,需要手动计算元素的实际高度,性能不好 | 都可用,在现代的设备上这点性能问题应该问题不大 |
css 真的是一门永远都学不会的语言,期待将来能有更多好用的功能,并且各个浏览器都给支持上。
最后推荐一下低代码平台我的应用,可以直接去下载后私有化部署且完全免费。开源不易,望多多支持,也可通过平台提出宝贵意见,感谢!