掌握流畅的 UI 过渡:‘Height: Auto’ hack 的终结
Source: Dev.to
在本文中,我想向你展示如何最终解决 CSS 开发中最令人头疼的难题之一:为具有动态高度的元素添加动画。
如果你已经在网页开发领域耕耘了一段时间,你一定体会过为手风琴或下拉菜单添加动画时的挫败感。多年来,我们只能依赖诸如将 max-height 动画到一个巨大的、随意的数值,或引入 JavaScript 库专门去测量像素等“技巧”。它们能工作,但在我看来从来都不是“正确的做法”。
今天,CSS 引入了两个新属性 interpolate-size 和 transition-behavior,可以原生处理这些数学计算和逻辑。下面我们一起看看如何实现它们。
什么是 “Auto” 问题?
问题从来不是动画本身,而是数学。浏览器擅长计算 0px 与 500px 之间的距离,但它们传统上无法计算 0px 与像 auto 这样的关键字之间的中点。
当你切换类时,浏览器会直接 “snap” 到最终高度,因为它没有数值路径可遵循。
1. 使用 interpolate-size 动画内在尺寸
为了修正计算,我们使用 interpolate-size。可以把它看作是给浏览器权限,让它能够计算“不可计算的”。
实现此功能的最佳方式是在 :root 级别进行设置。由于它是一个可继承的属性,只需一次启用,便可在站点的所有组件中生效。
/* root opt‑in */
:root {
/* This tells the engine to allow animations for keywords like auto or fit-content */
interpolate-size: allow-keywords;
}
.accordion-content {
height: 0;
overflow: hidden;
transition: height 0.4s ease-in-out;
}
.accordion-content.is-open {
/* Thanks to the root property, this now slides smoothly */
height: auto;
}
2. 使用 transition-behavior 处理离散状态
即使已经解决了高度问题,我们仍然需要处理 display 属性。为了保持 DOM 整洁并提升可访问性,通常在元素隐藏时会使用 display: none。
问题出在哪里?display 是一个离散属性——要么存在要么不存在。通常情况下,如果直接切换它,元素会瞬间消失,从而在高度或不透明度的平滑过渡进行到一半时被中断。
transition-behavior: allow-discrete 就是解决方案。它告诉浏览器在过渡持续时间结束之前,先不要真正切换 display,等动画完成后再进行切换。
.dropdown {
display: none;
opacity: 0;
/* 'allow-discrete' ensures the element stays 'block' until the fade ends */
transition:
display 0.3s allow-discrete,
opacity 0.3s;
}
.dropdown.is-open {
display: block;
opacity: 1;
}
3. 实践应用:“正确的方式”
当我们将这些结合起来时,就能得到一个性能优秀、可访问且流畅的组件。下面展示了我通常如何构建一个既需要滑动又需要淡入淡出的面板。
我将从左到右解释这些过渡值:首先定义属性(例如 display),然后是持续时间(0.5s),最后是行为(allow-discrete)。
:root {
interpolate-size: allow-keywords;
}
.panel {
display: none;
height: 0;
opacity: 0;
overflow: hidden;
/* Walkthrough: display waits for the others, height slides, opacity fades */
transition:
display 0.5s allow-discrete,
height 0.5s,
opacity 0.5s;
}
.panel.is-active {
display: block;
height: auto;
opacity: 1;
}
4. 实际应用与支持
我非常倡导渐进增强。只要回退方案可接受,我们不必等到 100 % 支持才开始使用这些工具。
transition-behavior:现在非常稳定(约 89 % 支持率)。已在 Chrome、Firefox 和 Safari 中可以正式使用。interpolate-size:较新(约 71 % 支持率)。在 Chromium 浏览器中表现出色,其他引擎也在快速跟进。
如果用户使用的是旧版浏览器,他们不会看到布局破碎;面板只会立即弹开。根据我的经验,这对于编写更简洁、更易维护的 CSS 来说,是一个完全可以接受的回退方案。
如果你想更加谨慎,可以在根元素的 opt‑in 前使用特性查询:
@supports (interpolate-size: allow-keywords) {
:root {
interpolate-size: allow-keywords;
}
}
这种做法让我们能够摆脱旧的 1000px max-height hack,编写真正实现预期效果的代码。下一个项目中试一试吧——这是一种更令人满意的构建方式。