过渡到自动高度

Avatar of Geoff Graham
Geoff Graham

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 $200 免费积分!

我知道这是 Chris 一直想要的,所以他当天就发表了一篇 很棒的文章,这并不奇怪。事实上,我最初是从他的帖子中了解到这个消息,而且无法找到任何公告。所以,我觉得有必要记下一些笔记,因为它感觉是一个重要的发展。

消息是:现在可以过渡到auto了!好吧,它将会成为现实。Chrome Canary 最近发布了对它的支持,目前只有在那里才能找到它。即使如此,我们仍然不知道 Chrome Canary 的实现是否会在该功能正式发布时成为语法的一部分。

问题

情况是这样的。您有一个元素。您已经对其进行了标记,放入了内容,并应用了许多样式。您知道它有多高吗?当然不知道!当然,我们可以让 JavaScript 为我们评估该元素,但就 CSS 而言,元素的计算尺寸是未知的。

这使得例如将该元素从height: 0动画到height: whatever变得困难。我们需要知道“whatever”是什么,而我们只能通过在元素上设置固定高度来做到这一点。这样,我们就有数字可以从零高度过渡到那个特定高度。

.panel {
  height: 0;
  transition: height 0.25s ease-in;

  &.expanded {
    height: 300px;
  }
}

但是如果该元素随着时间的推移而发生变化会怎么样?也许字体发生了变化,我们添加了填充,插入了更多内容……任何更改尺寸的事情。我们可能需要将height: 300px更新为任何最适合的新固定高度。这就是为什么我们经常看到 JavaScript 用于切换尺寸扩展和收缩的东西,以及其他解决方法。

我说这是关于height属性,但我们也在讨论逻辑等效的block-size,以及widthinline-size。或者任何方向!

过渡到auto

这是目标,对吧?当高度尺寸未知时,我们倾向于使用height: auto。从那里,我们让 JavaScript 计算它的评估结果,并从那里开始。

当前的 Chrome 实现使用 CSS calc-size() 来完成繁重的工作。它识别auto关键字,并根据它的名字,计算出那个数字。换句话说,我们可以这样做,而不是使用固定高度方法

.panel {
  height: 0;
  transition: height 0.25s ease-in;

  &.expanded {
    height: calc-size(auto);
  }
}

就是这样!当然,calc-size() 能够处理更复杂的表达式,但事实上我们只需要向它提供一个关于元素高度的模糊关键字就令人印象深刻。这使我们能够从固定值过渡到元素的内在大小,然后再返回。

我不得不试一试。我确信这里有很多用例,但我使用了一个日历组件中的浮动按钮,它表示待定的日历邀请数量。单击该按钮,一个面板会在日历上方展开,并显示邀请。再次单击它,面板会返回到它来的地方。JavaScript 处理单击交互,触发一个类更改,该更改在 CSS 中过渡高度。

如果您不想打开 Canary,可以观看一个视频

这是相关的 CSS

.invite-panel {
  height: 0;
  overflow-y: clip;
  transition: height 0.25s ease-in;
}

单击时,JavaScript 会在元素上设置自动高度作为内联样式,以覆盖 CSS

<div class="invite-panel" style="height: calc(auto)">

CSS 中的transition属性让浏览器知道我们打算在某个时候更改height属性,并使其变得平滑。并且,与任何过渡或动画一样,最好通过 使用prefers-reduced-motion减慢或删除运动来考虑运动敏感度。

display: none怎么样?

这是我读到 Chris 的帖子后第一个想到的问题,他也在文章中提到了这一点。从一个元素从display: none过渡到它的内在大小,有点像从height: 0过渡。看起来非显示元素的高度为零,但实际上它的计算高度为auto,除非在它上面声明了特定高度。

DevTools showing computed values for an element with display none. The height value shows as auto.

因此,如果我们想从 CSS 中的display: none进行过渡,还需要做额外的工作。我将直接插入 Chris 分享的代码,因为它很好地展示了关键部分

.element {
  /* hard mode!! */
  display: none;

  transition: height 0.2s ease-in-out;
  transition-behavior: allow-discrete;

  height: 0; 
  @starting-style {
    height: 0;
  }

  &.open {
    height: calc(auto);
  }
}
  • 元素一开始既有display: none又有height: 0
  • 有一个.open类,它将元素的高度设置为calc-size(auto)

这是我们要连接的两个点,我们首先在元素上设置transition-behavior: allow-discrete。这对我来说是新的,但 规范指出transition-behavior“指定是否会为离散属性启动过渡”。当我们声明allow-discrete时,“过渡将为离散属性以及可插值属性启动”。

好吧,DevTools 就在那里向我们展示了height: auto是一个离散属性!但是,请注意@starting-style声明。如果您不熟悉它,那么您并不孤单。这个想法是,它允许我们为过渡设置一个“开始”样式。由于我们元素的离散高度是auto,我们需要告诉过渡从height: 0开始,而不是auto

.element {
  /* etc. */

  @starting-style {
    height: 0;
  }
}

现在,我们可以从零过渡到auto,因为我们有点用@starting-style覆盖了离散高度。能够做到这一点非常酷!

直接链接 →