理解和設定寬高比
渲染到頁面上的每個元素都有高度和寬度,因此也有寬高比,即寬度和高度之比。媒體物件的自然尺寸,即在沒有任何尺寸調整、縮放、放大或邊框應用的情況下的尺寸,被稱為其自然尺寸或固有尺寸。元素的固有尺寸由元素本身決定,而不是透過應用格式(如 box sizing)或設定邊框、外邊距或內邊距寬度來決定的。
在開發網站時,你經常希望能夠將元素的寬度設定為視口或父容器尺寸的百分比,並讓高度按比例變化,從而根據視口的大小保持特定的寬高比。對於像圖片和影片這樣的替換元素,保持特定的寬高比不僅是建立響應式網頁設計所必需的,也是提供良好使用者體驗的重要組成部分。為資源設定寬高比可以防止載入抖動(jank)——即在頁面已經繪製後媒體載入時發生的佈局偏移,由於沒有為資源預留空間而導致重排。
使用 CSS,你可以根據寬高比調整替換元素和非替換元素的尺寸。在本指南中,我們將學習 aspect-ratio 屬性,討論替換元素和非替換元素的寬高比,然後研究一些常見的寬高比用例。
aspect-ratio 屬性如何工作
CSS aspect-ratio 屬性值定義了元素盒子的首選寬高比。該值可以是 <ratio>、關鍵字 auto,或兩者的空格分隔組合。
<ratio> 是寬度與高度的比率,順序是先寬後高。它由兩個正的 <number> 值,中間用正斜槓(/)分隔,或單個 <number> 來表示。當使用單個數字時,它等同於將比率寫為 <number> / 1,也就是寬度除以高度。
以下值都是等效的
aspect-ratio: 3 / 6;
aspect-ratio: 1 / 2;
aspect-ratio: 0.5 / 1;
aspect-ratio: 0.5;
以下值也都是等效的
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> 比率則用作首選寬高比。
你會注意到在上述定義中出現了“首選”一詞。設定的 aspect-ratio 值並非總是會生效。aspect-ratio 屬性設定的是一個“首選”寬高比,因此只有在盒子的至少一個尺寸為自動時才會生效。
當高度和寬度,或者說內聯尺寸和塊級尺寸都被顯式設定時,aspect-ratio 屬性值將被忽略。在這種情況下,不允許任何維度自動調整尺寸——首選尺寸已被明確設定——所以 aspect-ratio 屬性沒有效果。當你同時宣告內聯和塊級尺寸時,這些宣告會優先。
對於替換元素,如果你沒有為任一維度明確設定一個值(auto 除外),那麼這兩個維度都將預設為其固有尺寸(任何 aspect-ratio 值都不會被應用)。aspect-ratio 將應用於沒有明確設定維度的非替換元素,因為非替換元素要麼是固有尺寸,要麼是外部尺寸,其大小取決於其內容、容器、盒模型屬性等。
當一個元素渲染到頁面上時,如果沒有應用 CSS 且沒有包含 HTML 尺寸屬性,使用者代理將以其自然尺寸渲染該物件。
調整替換元素的寬高比
像 <img> 和 <video> 這樣的替換元素會被具有固定尺寸的媒體所替換,因此它們具有固有的寬高比。以光柵影像為例,如 JPEG、PNG 或 GIF。如果你在頁面上放置一張圖片,並且沒有透過 <img> 屬性或 CSS 設定高度或寬度,它將以其固有尺寸顯示。
這是一張 220px 的正方形圖片,沒有應用任何 CSS;它以其固有或預設尺寸顯示。
如果替換內容是自動調整尺寸的,或者你只為一個維度提供了尺寸,例如設定了 width 的值,瀏覽器會自動調整另一個維度(在這裡是高度),同時保持媒體的原始寬高比。
在此示例中,僅對圖片設定了 width,因此使用者代理保留了其寬高比。同一張圖片重複三次,分別以不同的寬度顯示:55px、110px,以及透過 width: auto 值顯示為其自然尺寸 220px。
只有當你為兩個維度都提供尺寸時,才存在扭曲替換元素的風險。例如,在一張圖片上設定 width: 100vw; 和 height: 100vh; 會建立一個可變的寬高比;當視口的寬高比與圖片的自然寬高比不同時,圖片會顯得被拉伸或壓扁。
在此示例中,同一張圖片重複三次,明確指定了相同的 height 值(110px),但 width 值不同(55px、110px 和 220px)。
我們透過同時設定 height 和 width 有意地扭曲了圖片:我們壓扁了第一張,拉伸了第三張。
我們本可以使用 CSS 的 aspect-ratio 屬性來建立同樣扭曲的效果,方法是設定單個維度(而不是兩個或都不設定)並提供一個非 1(或 1 / 1)的值。你可能不想這樣做,但知道這是可能的也很好。
img {
height: 90vh;
aspect-ratio: 3;
}
我們聲明瞭一個維度;100vh 是示例 <iframe> 視口的完整高度。要使 aspect-ratio 應用於替換元素,必須只設置一個維度。設定兩個或都不設定都行不通。
使替換元素適應其容器
為了使替換元素適應其容器的尺寸,同時保持其固有的寬高比,可以將 object-fit 屬性值設定為 cover 或 contain。這會調整替換元素的大小,並將其裁剪以“覆蓋”容器,或者以較小的尺寸顯示,完全“包含”在其中。
在此示例中,將正方形影像放置在一個由三個專案組成的網格中,每個專案的寬高比為 5 / 2。
首先,我們建立一個包含三個專案的容器,每個專案包含一張圖片。
<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 的寬度決定,而 iframe 的寬度將基於你的視口大小。
.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: #cccccc;
}
然後,我們調整圖片的大小,並對最後兩張圖片設定 object-fit 屬性。
img {
height: 100%;
width: 100%;
}
.cover {
object-fit: cover;
}
.contain {
object-fit: contain;
}
只有第一張圖片被扭曲(拉伸)了。我們本可以使用 object-fit 的 fill 值來建立同樣的效果。cover 圖片橫跨容器的整個寬度,垂直居中,並被裁剪以適應容器。contain 值確保圖片被包含在容器內,水平居中,並縮小以適應容器。
為非替換元素定義寬高比
雖然替換元素的寬高比預設會保持,但調整非替換元素的固有尺寸通常會改變其寬高比。例如,相同的內容在寬屏或寬父容器中可能顯示為三行,但在窄屏或窄容器中則可能顯示為八行。
在此示例中,同一段引用分別顯示在 200px 和 600px 寬的容器中,並且設定了一個正方形,其高度與其 200px 的寬度相匹配。
為了突出透過尺寸維度設定非替換元素寬高比的問題,請在 auto 和 visible 之間切換 overflow 屬性。
blockquote {
width: 200px;
}
blockquote:nth-of-type(2) {
width: 600px;
}
blockquote:nth-of-type(3) {
height: 200px;
}
雖然可以透過設定兩個維度並隱藏溢位內容來為非替換元素定義寬高比,但 CSS 的 aspect-ratio 屬性提供了明確的寬高比支援。這意味著即使你不知道內容或螢幕尺寸,也可以設定特定的寬高比。
在下一個示例中,我們透過在非替換元素 <blockquote> 上設定 aspect ratio: 1 來渲染正方形盒子,而不管文字的寬度如何。
blockquote {
inline-size: max-content;
aspect-ratio: 1;
}
每個盒子都定義了一個維度:inline-size(在水平語言中即寬度)被設定為 max-content,這將尺寸設定為足夠寬以容納內容而無需換行。第二個維度,在這裡是 block-size 或 height,被設定為與第一個維度相同的長度。這是透過 aspect-ratio 屬性實現的。我們將元素盒子的期望寬高比定義為 1,這與 1 / 1(正方形)相同。這使得塊級方向的尺寸與元素的寬度相匹配,而無需使用 height 或 block-size 屬性。
在這些示例中,尺寸被明確地設定在元素本身上。處理非替換元素時,當沒有明確設定尺寸維度時,寬高比就會發揮作用。
根據容器尺寸建立一個圓形
非替換塊級元素的內聯尺寸是其容器內容盒的大小。因為它們預設具有尺寸,所以不需要為 aspect-ratio 屬性設定顯式尺寸就能生效。
在此示例中,我們有一個 200px 寬的容器 <div>,其中每側包含 5px 的內邊距。因此,內容盒的內聯尺寸為 190px。在不為巢狀的 <p> 元素設定高度或寬度的情況下,我們知道其內聯尺寸是 190px。設定了 aspect-ratio: 1 後,該段落將是 190px 高,除非有可見的溢位內容使其更高(但實際上沒有)。
<div> 元素的高度沒有明確設定,但它包含了 190px 高的段落、頂部和底部的 5px 內邊距,以及 <p> 的預設頂部和底部外邊距的總高度。因此,它比它的寬度要高。兩個元素都設定了 50% 的 border-radius,所以容器是一個橢圓形,而子元素因為 aspect-ratio 為 1 且沒有明確定義內聯或塊級尺寸,所以是一個圓形。
<div><p>Hello world</p></div>
div,
p {
border-radius: 50%;
}
div {
width: 200px;
padding: 5px;
border: 1px solid black;
background-color: #66ccff;
}
p {
aspect-ratio: 1;
text-align: center;
border: 10px solid white;
background-color: #f4aab9;
}
要使 <div> 成為一個圓形,我們可以將 height 和 width 設定為相同的值,或者設定 aspect-ratio: 1 並將 overflow 設定為 auto 或 hidden。另外,我們也可以簡單地用 margin-block: 0 移除段落的外邊距。下面展示了這兩種選項。
<div><p>Hello world</p></div>
<div><p>Hello world</p></div>
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。
.youtube {
aspect-ratio: 16/9;
}
.instagram,
.tiktok {
aspect-ratio: 9/16;
}
我們可以在 @media 查詢中使用 aspect-ratio 特性,並結合 aspect-ratio 屬性來調整 iframe 及其所含影片的大小。這確保了影片內容始終儘可能大——佔據視口的全部寬度或高度,而不管視口大小如何——同時保持特定的寬高比。
我們可以將橫向的 YouTube 影片設定為視口寬度,將縱向的 TikTok 和 Instagram 影片 iframe 設定為視口高度。如果視口的寬高比大於 16:9,我們將 YouTube 影片設定為視口高度。如果視口窄於 9:16,我們將 Instagram 和 TikTok 影片都設定為視口寬度。
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 決定,以匹配其寬度。
.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%,來確保該內容不會大於網格專案。
.item {
min-height: 0;
overflow: auto;
}
.item > * {
max-height: 100%;
}
規範
| 規範 |
|---|
| CSS Box Sizing Module Level 3 # aspect-ratio |