写完了八年 CSS,每次想给折叠面板加个展开动画都要靠 JS 读 scrollHeight——今天这件事被一行 CSS 彻底原生化了
写完了八年 CSS,每次想给折叠面板加个展开动画都要靠 JS 读 scrollHeight——今天这件事被一行 CSS 彻底原生化了
配了八年 CSS,有一个问题折磨了前端工程师整整八年:怎么给 height: auto 加过渡动画?
传统上做不到。CSS 动画要求两个端点都能计算出具体数值,但 auto 不是一个数——它是浏览器根据内容算出来的。既然算不出来,过渡就只能是「跳」而不是「滑」。
这个问题最常出现在折叠面板、抽屉、下拉菜单上。你写了一个 .panel { height: 0; overflow: hidden; transition: height 0.3s; } 然后满心期待它能滑开,结果动画根本不触发——直接闪现到最终状态。所有人都只能靠 JS:先读 scrollHeight,设成固定像素值,等过渡结束再改回 auto。十行代码,只为骗浏览器相信这应该是个动画。
Chrome 129 把这件事用一个属性彻底原生化了。
interpolate-size:让 auto 参与过渡
interpolate-size 是 CSS Values Level 5 的属性,它的默认值是 numeric-only,意思是不允许关键字参与过渡。把值设成 allow-keywords 就等于告诉浏览器:「我允许 auto / min-content / max-content / fit-content 这些关键字参与数值插值。」
一行代码:
:root {
interpolate-size: allow-keywords;
}
.panel {
height: 0;
overflow: hidden;
transition: height 0.3s ease;
}
.panel.open {
height: auto; /* 现在能动画了 */
}
.panel 从 height: 0 到 height: auto 的过渡,从「闪现」变成「滑开」。浏览器在动画开始时把 auto 解析成当前内容的实际像素高度,然后在这两个数值之间做平滑插值。
calc-size():对 auto 做数学
interpolate-size: allow-keywords 解决了「能不能」的问题,但如果你想在 auto 基础上加一个固定值呢?比如「展开高度比内容多 2rem」?这在以前只能靠 JS 算。
calc-size() 函数是 Chrome 129 同时带来的新语法,它允许你在 intrinsic size 关键字上做运算:
.panel.expanded {
/* 在 auto 的实际高度上加 2rem */
height: calc-size(auto, size + 2rem);
}
calc-size(auto, size + 2rem) 的意思是:把 auto 解析成具体数值(记作 size),然后在 size 基础上加 2rem。这就是「对 auto 做数学」。
同样适用于宽度:
.nav-item {
width: 80px;
overflow-x: clip;
transition: width 0.35s ease;
}
.nav-item:hover {
/* 展开到内容的自然宽度 */
width: calc-size(auto, size);
}
鼠标悬停时导航项宽度从固定值平滑过渡到内容自适应宽度,不用任何 JS。
三个坑
第一个:不能关键字到关键字。动画的一端必须是具体数值或百分比,不能两端都是关键字。height: auto → height: min-content 是不支持的。
第二个:每次重新布局有性能代价。随着内容高度变化,浏览器要实时重新计算布局再插值,动画大内容块时需要注意。测试过复杂页面再决定是否全局开启 interpolate-size。
第三个:渐进增强必须做。Firefox 135 以下和 Safari 18.2 以下不支持 fallback 浏览器只是闪现,没有功能损失。渐进增强写法:
/* 支持的浏览器:平滑过渡 */
@supports (interpolate-size: allow-keywords) {
:root { interpolate-size: allow-keywords; }
.panel { height: auto; transition: height 0.3s ease; }
}
/* 不支持的浏览器:用 grid hack 优雅降级 */
@supports not (interpolate-size: allow-keywords) {
.panel {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease;
}
.panel > .content { overflow: hidden; }
.panel.open { grid-template-rows: 1fr; }
}
grid-template-rows: 0fr → 1fr 是另一个实现折叠动画的纯 CSS 方法,只是没有 interpolate-size 那么直观。两者配合用,兼容性覆盖到所有现代浏览器。
三步下一步
第一步:Chrome DevTools 打开一个折叠面板,用 Styles 面板把 height 从 0 改成 auto,看它是不是直接闪现——如果闪现,说明你的页面还没开 interpolate-size。
第二步:在 :root 加一行 interpolate-size: allow-keywords;,刷新,再试。如果滑开动画了,把这个声明写进你的全局 CSS reset。
第三步:找一个项目里的折叠组件,把 JS 里的 scrollHeight 测量代码删掉,用 height: auto 过渡替代。先用 @supports 做渐进增强,生产验证三周后再全局推广。
浏览器支持:Chrome/Edge 129+、Safari 18.2+、Firefox 135+。三个主流浏览器全部稳定,覆盖全球约 68%,窗口期正好。
评论区
登录后可评论。