理解和设置纵横比

渲染到页面上的每个元素都有高度和宽度,因此具有 纵横比,它是宽度与高度的比率。媒体对象的自然尺寸(即未应用任何尺寸、缩放、缩放或边框时的尺寸)被称为其自然尺寸或 固有尺寸。元素的固有尺寸由元素本身决定,而不是由应用格式(如 盒子大小)或设置边框、边距或填充宽度来决定。

在开发网站时,您通常希望能够将元素的宽度设置为视窗或父容器大小的百分比,并使高度按比例改变大小,从而保持特定纵横比(取决于视窗的大小)。对于替换元素(如图像和视频),保持特定纵横比不仅对于创建 响应式网页设计 是必要的,而且也是提供良好用户体验的重要组成部分。设置资产的纵横比可以防止加载 卡顿——在页面已绘制后媒体加载时发生的布局偏移,导致回流,因为没有为资产保留空间。

使用 CSS,您可以根据纵横比调整替换元素和非替换元素的大小。在本指南中,我们将学习 `aspect-ratio` 属性,讨论替换元素和非替换元素的纵横比,然后研究一些常见的纵横比用例。

aspect-ratio 属性的工作原理

CSS `aspect-ratio` 属性值定义了元素盒子首选的宽度与高度比率。该值为 `<ratio>`、关键字 `auto` 或两者组合,用空格分隔。

`<ratio>` 是宽度与高度的比率,按此顺序。它由用正斜杠 (/) 分隔的两个正 `<number>` 值或单个 `<number>` 表示。当使用单个数字时,它与将比率写为 `<number> / 1` 相同,这也是宽度除以高度的结果。

以下值都等效

css
aspect-ratio: 3 / 6;
aspect-ratio: 1 / 2;
aspect-ratio: 0.5 / 1;
aspect-ratio: 0.5;

以下值也都等效

css
aspect-ratio: 9/6;
aspect-ratio: 3/2;
aspect-ratio: 1.5;

`auto` 关键字的效果取决于应用它的元素是否为替换元素。对于具有固有纵横比的替换元素,`auto` 表示应使用固有纵横比。在所有其他情况下,`auto` 值表示盒子没有首选纵横比。在这两种情况下,这是默认行为,就像没有应用 `aspect-ratio` 属性一样。

当该值包含 `auto` 关键字和 `<ratio>` 值时,例如 `aspect-ratio: auto 2 / 3;` 或 `aspect-ratio: 0.75 auto;`,则 `auto` 值应用于具有自然纵横比的替换元素,而 `width / height` 或 `<number>` 指定的比率用作首选纵横比。

您可能已经注意到上述定义中出现了“preferred”这个词。aspect-ratio 值并不总是被应用。aspect-ratio 属性设置的是一个“首选”的纵横比,因此只有当至少一个盒子的尺寸是自动时才起作用。

当高度和宽度或内联和块尺寸都明确设置时,aspect-ratio 属性值将被忽略。在这种情况下,不允许任何维度自动调整大小 - 首选大小是明确设置的 - 因此 aspect-ratio 属性不起作用。当您声明内联和块尺寸时,它们将优先于 aspect-ratio

对于替换元素,如果您没有显式设置任何维度值(除了 auto),则两者都将默认为其固有尺寸(任何 aspect-ratio 值都不会被应用)。aspect-ratio 将应用于没有显式设置维度的非替换元素,因为非替换元素是 固有外在 调整尺寸,从其内容、容器、盒子模型 属性等获得其尺寸。

当元素渲染到页面时,如果没有应用任何 CSS 并且没有包含任何 HTML 尺寸属性,用户代理将以其自然尺寸渲染对象。

调整替换元素的纵横比

替换元素,例如 <img><video>,会被替换为具有设定尺寸的媒体,因此具有固有纵横比。例如,考虑一个光栅图像,如 JPEG、PNG 或 GIF。如果将图像放在页面上,并且没有通过 <img> 属性或 CSS 设置高度或宽度,它将以其固有尺寸显示。

这是一个没有应用 CSS 的 220px 正方形图像;它以其固有或默认尺寸显示。

如果替换内容是自动调整尺寸的,或者您只为一个维度提供尺寸,例如设置宽度值,浏览器将自动调整另一个维度,在本例中为高度,同时保持媒体的原始纵横比。

在这个例子中,只有图像的 width 被设置,所以用户代理会保留其纵横比。相同的图像被重复了三次,以不同的宽度显示:55px110px,以及通过 width: auto 值实现的自然尺寸 220px

只有当您为两个维度都提供尺寸时,才会存在扭曲替换元素的风险。例如,在图像上设置 width: 100vw;height: 100vh; 会创建一个可变的纵横比;当视窗的纵横比不同于图像的自然纵横比时,图像将看起来拉伸或压缩。

在这个例子中,相同的图像被重复了三次,显式地设置了相同的 height 值 (110px),但 width 值不同 (55px110px220px)。

我们有意地通过设置 heightwidth 来扭曲图像:我们压缩了第一个图像,拉伸了第三个图像。

我们可以使用 CSS 的 aspect-ratio 属性创建相同的扭曲效果,方法是设置一个维度(而不是两个或都不设置),并提供一个非 1(或 1 / 1)的值。您可能不想这样做,但知道它是可能的。

css
img {
  height: 100vh;
  aspect-ratio: 3;
}

我们声明了一个维度;100vh 是示例 <iframe> 视窗的完整高度。为了使 aspect-ratio 应用于替换元素,必须只设置一个维度。设置两个维度或都不设置都不起作用。

将替换元素放入容器中

要将替换元素调整到其容器的尺寸,同时保持其固有纵横比,请将 object-fit 属性值设置为 covercontain。这将调整替换元素的大小,并将其裁剪以“覆盖”容器,或以较小的尺寸显示,完全“包含”在容器中。

在这个例子中,正方形图像被放置在一个包含三个项目的网格中,每个项目都有 5 / 2 的纵横比。

首先,我们创建一个包含三个项目的容器,每个项目包含一个图像

html
<div class="grid">
  <div>
    <img
      src="https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg"
      alt="Pride flag" />
  </div>
  <div>
    <img
      class="cover"
      src="https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg"
      alt="Pride flag" />
  </div>
  <div>
    <img
      class="contain"
      src="https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg"
      alt="Pride flag" />
  </div>
</div>

接下来,我们将容器指定为网格,其中每个项目的纵横比为 2.5 (5/2),最小宽度为 150px。因此,最小高度将为 60px。但是,最终的宽度和高度由示例 iframe 的宽度决定,该宽度将基于您的视窗大小

css
.grid {
  display: grid;
  gap: 20px;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  font-size: 0; /* to minimize whitespace */
}

div div {
  aspect-ratio: 5 / 2;
  background-color: #ccc;
}

然后,我们调整图像大小,并在最后两个图像上设置 object-fit 属性

css
img {
  height: 100%;
  width: 100%;
}

.cover {
  object-fit: cover;
}

.contain {
  object-fit: contain;
}

只有第一个图像被扭曲(拉伸)。我们也可以使用 object-fitfill 值来创建相同的效果。cover 图像跨越容器的整个宽度,垂直居中,并被裁剪以适合容器。contain 值确保图像包含在容器内,水平居中,并缩小以适合容器。

为非替换元素定义纵横比

虽然替换元素的纵横比默认情况下会保持,但调整非替换元素的固有尺寸通常会改变其纵横比。例如,相同的内容在宽屏或宽父容器上可能显示为三行,但在窄屏或容器上可能显示为八行。

在这个例子中,相同的引文在 200px600px 宽的容器中显示,并且一个正方形被设置为具有与 200px 宽度匹配的高度

为了突出显示通过尺寸维度设置非替换元素的纵横比的问题,请在 autovisible 之间切换 overflow 属性。

css
blockquote {
  width: 200px;
}

blockquote:nth-of-type(2) {
  width: 600px;
}

blockquote:nth-of-type(3) {
  height: 200px;
}

虽然可以通过设置两个维度并隐藏溢出内容来在非替换元素上定义纵横比,但 CSS 的 aspect-ratio 属性提供了显式纵横比支持。这意味着即使您不知道内容或屏幕尺寸,也可以设置特定的纵横比。

在下一个例子中,我们通过在 <blockquote>(一个非替换元素)上设置 aspect ratio: 1 来渲染正方形框,无论文本的宽度如何

css
blockquote {
  inline-size: max-content;
  aspect-ratio: 1;
}

每个框都定义了一个维度:inline-size(在水平语言中是宽度)被设置为 max-content,该值将尺寸设置为适合内容而不会换行所需的宽度。第二个维度(在本例中为 block-sizeheight)被设置为与第一个维度相同长度。这是通过 aspect-ratio 属性实现的。我们定义了元素框的期望宽高比为 1,与 1 / 1 相同,即正方形。这将块方向设置为与元素宽度匹配,而无需使用 heightblock-size 属性。

在这些例子中,尺寸是在元素本身被显式设置的。当使用非替换元素时,纵横比会在没有显式设置尺寸维度时起作用。

根据容器大小创建圆形

非替换块级元素的内联尺寸与其容器的 内容框 的尺寸相同。由于它们默认情况下具有尺寸,因此它们不需要显式设置尺寸以使 aspect-ratio 属性起作用。

在这个例子中,我们有一个 200px 宽的容器 <div>,它包含每边 5px 的填充。因此,内容框的内联尺寸为 190px。在没有在嵌套的 <p> 元素上设置高度或宽度的情况下,我们知道其内联尺寸为 190px。设置了 aspect-ratio: 1 后,段落将为 190px 高,除非它有可见的溢出内容导致它更高(它没有)。

<div> 元素的高度没有被显式设置,但它包含 190px 高的段落、顶部和底部的 5px 填充以及 <p> 的默认顶部和底部边距的组合高度。因此,它比宽度高。这两个元素都有 border-radius50%,因此容器是椭圆形,而子元素,其 aspect-ratio1,但没有显式定义内联或块尺寸,是圆形。

html
<div><p>Hello world</p></div>
css
div,
p {
  border-radius: 50%;
}

div {
  width: 200px;
  padding: 5px;
  background-color: #66ccff;
}

p {
  aspect-ratio: 1;
  text-align: center;
  border: 10px solid #ffffff;
  background-color: #f4aab9;
}

为了使 <div> 成为圆形,我们可以将 heightwidth 设置为相同的值,或者设置 aspect-ratio: 1 并将 overflow 设置为 autohidden。或者,我们可以简单地使用 margin-block: 0 删除段落的边距。这两个选项都显示在下面。

html
<section>
  <div><p>Hello world</p></div>
  <div><p>Hello world</p></div>
  <section></section>
</section>
css
div,
p {
  aspect-ratio: 1;
  border-radius: 50%;
}

div:first-of-type {
  overflow: hidden;
}

div:last-of-type p {
  margin-block: 0;
}

常见 aspect-ratio 用例

让我们看看一些可以使用 aspect-ratio 来解决创建响应式设计时遇到的一些常见挑战的情况。

使外部资源响应式

所有内容都应该响应式,即使这些内容是第三方嵌入内容,例如来自 TikTok、YouTube 或 Instagram 的视频。您用来嵌入这些外部视频的代码片段通常会创建一个 <iframe>

虽然 <video> 元素通常会采用其媒体文件的纵横比,但 iframe 元素没有这种功能。这带来了确保 <iframe> 响应式,同时始终保持其包含的视频的纵横比的挑战。我们可以使用的一种技术是将 iframe 的宽度设置为其容器的 100%100vw,以匹配视窗宽度,而不管视窗的大小。但是,设置固定高度可能会拉伸或压缩视频。相反,我们在视频容器上设置 aspect-ratio,使其与视频的纵横比保持一致。问题解决!

作为背景,YouTube 视频的标准纵横比在台式机或笔记本电脑上观看时为 16:9,而 TikTok 和 Instagram 视频的纵横比为 9:16。

css
.youtube {
  aspect-ratio: 16/9;
}

.instagram,
.tiktok {
  aspect-ratio: 9/16;
}

我们可以在 @media 查询中使用 aspect-ratio 功能,并结合 aspect-ratio 属性来调整 iframe 及其包含的视频的大小。这确保视频内容始终尽可能大 - 占用视窗的整个宽度或高度 - 同时保持特定的纵横比,而不管视窗的大小。

我们可以将横向的 YouTube 视频设置为与视窗一样宽,将纵向的 TitTok 和 Instagram 视频 iframe 设置为与视窗一样高。如果视窗的纵横比大于 16:9,我们将 YouTube 视频设置为与视窗的高度相同。如果视窗的宽度小于 9:16,我们将 Instagram 和 TikTok 视频都设置为与视窗的宽度相同。

css
iframe.youtube {
  aspect-ratio: 16/9;
  width: 100vw;
  height: auto;
}

iframe.instagram,
iframe.tiktok {
  aspect-ratio: 9/16;
  height: 100vh;
  width: auto;
}

/* If the viewport is very wide but not very tall */
@media (aspect-ratio > 16 / 9) {
  iframe.youtube {
    width: auto;
    height: 100vh;
  }
}

/* If the viewport is very tall but not very wide */
@media (aspect-ratio < 9 / 16) {
  iframe.instagram,
  iframe.tiktok {
    height: auto;
    width: 100vw;
  }
}

使网格单元格成为正方形

可以通过定义固定列轨道尺寸来创建正方形单元格网格,确保每行都匹配列轨道的尺寸。但是,当使用auto-fill创建响应式网格以在容器中尽可能多地容纳列轨道时,每个项目的宽度就会变得不确定。这使得难以确定创建正方形项目的适当高度。

通过在项目上设置纵横比,我们可以确保在网格项目布局时,每个网格项目的高度与其宽度相同,从而创建无论容器尺寸如何都是正方形的网格项目。

在此正方形网格项目示例中,网格轨道是自动大小的,其大小取自项目。每个项目至少为95px宽,但可能更宽。无论宽度如何,每个项目都将是一个正方形,其高度由aspect-ratio确定以匹配其宽度。

css
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(95px, 1fr));
}

.item {
  aspect-ratio: 1;
}

为了使网格项目的内容不会超过aspect-ratio设置的首选高度,请将min-height设置为0,并将overflow设置为除visible以外的值。这将适用于内在大小的内容。如果您有内在地大于可用空间的内容,请通过将max-height(或max-width,取决于内容)设置为100%来设置该内容不超过网格项目的大小。

css
.item {
  min-height: 0;
  overflow: auto;
}

.item > * {
  max-height: 100%;
}

另请参见