前几天我遇到一个需要制作宽高比友好框的小问题。 这并不是什么新鲜事。 我想,最初的功劳要追溯到 2009 年,Thierry Koblentz 的 Intrinsic Ratios,并一直流行至今,甚至适用于其他类型的文章内容,例如 Uncle Dave’s Ol’ Padded Box。
让我们一起探索一下这个概念,因为有很多内容值得讨论。
核心概念:百分比填充基于宽度
即使这有点反直觉,比如垂直填充。 这不是一个黑客技术,但它确实很奇怪:padding-top
和 padding-bottom
基于父元素的 width
。 因此,如果你的元素宽度为 500px,padding-top
为 100%,那么 padding-top
将为 500px。
这难道不是一个完美的正方形吗,500px × 500px? 没错,它是一个正方形! 一个宽高比!
如果我们强制元素高度为零(height: 0;
)并且没有边框。 那么填充将是影响高度的盒子模型的唯一部分,我们将得到我们的正方形。
现在,想象一下,我们没有使用 100% 的顶部填充,而是使用了 56.25%。 这恰好是一个完美的 16:9 比例!(9 / 16 = 0.5625)。
现在我们有了 一个友好的宽高比框,它在流体宽度环境中效果很好。 如果宽度改变,高度也会改变,元素会保持这个宽高比。
用例:背景图像
也许我们制作了一个 文字排版锁住。 它用于文章的标题,因此使用 <h1>
标签是有意义的。
<h1>
Happy Birthday
</h1>
我们可以将 <h1>
标签设为宽高比框,并将排版锁住作为背景图像应用。
h1 {
overflow: hidden;
height: 0;
padding-top: 56.25%;
background: url(/images/happy-birthday.svg);
}
但我在上面关于宽高比的描述有点错误。 它实际上并不是一个 16:9 的图像。 我只是从一个图片网站下载了这个图形。 它恰好是一个具有 viewBox="0 0 1127.34 591.44"
属性的 SVG,这意味着它本质上是一个 1127.34 × 591.44 的图像(在宽高比方面)。 或者它可能是一个 328 × 791 的图像。
我想说,任何随机图像都不符合非常特定的预定义宽高比的情况非常常见……
任何可能的宽高比的数学
正方形和 16:9 的比例很好,但用于这些比例的值只是简单的数学运算。 宽高比可以是任何东西,它们通常是完全任意的。 视频或图像可以裁剪成任何尺寸。
那么,我们如何计算上面 1127.34 × 591.44 SVG 的 padding-top
呢?
一种方法是使用 calc()
,像这样
padding-top: calc(591.44 / 1127.34 * 100%);
不久前有人告诉我,在这里使用 calc()
可能“更慢”,但我从未见过任何证据。 我想,是的,计算机确实需要计算一些东西,所以在一个与不需要计算的情况的正面交锋中,计算会更慢。 但一个数学问题对于计算机来说似乎并不是太大的工作量。 例如,流行的英特尔奔腾 III(1999 年发布)可以达到 2,054 MIPS 或“每秒百万条指令”,所以它可以非常快地完成除法运算。 现在,芯片的速度快了 50 倍。
如果我们使用像 Sass 这样的预处理器,我们可以提前进行计算
padding-top: 591.44px / 1127.34px * 100%;
无论哪种方式,我都喜欢将数学运算保留在作者的代码中。
如果填充将所有内容向下推,如何将内容放在里面?
我们在上面的演示中隐藏了内容,让内容被推出去,并隐藏了溢出。 但如果需要一个宽高比框,同时将内容保留在框内呢? 这有点棘手。 我们需要将它定位回原位。 绝对定位可以胜任这个任务。
假设我们现在只使用文本,但仍然需要一个宽高比框。 我们需要一个用于绝对定位的内部包装器。 让我们使用具体的类名
<h1 class="aspect-ratio-box">
<div class="aspect-ratio-box-inside">
Happy Birthday
</div>
</h1>
然后执行定位
.aspect-ratio-box {
height: 0;
overflow: hidden;
padding-top: 591.44px / 1127.34px * 100%;
background: white;
position: relative;
}
.aspect-ratio-box-inside {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
为了好玩,让我们在这里全力以赴,居中文本并调整其大小,使其与框一起缩放
<h1 class="aspect-ratio-box">
<div class="aspect-ratio-box-inside">
<div class="flexbox-centering">
<div class="viewport-sizing">
Happy Birthday
</div>
</div>
</div>
</h1>
还需要一些其他类来进行样式设置
.flexbox-centering {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.viewport-sizing {
font-size: 5vw;
}
用例:视频
这可能是所有这些宽高比框技术中最常见和最实用的用例。 HTML5 <video>
并没有问题,因为它表现得像 <img>
一样具有宽高比。 但网络上的许多视频都放在 <iframe>
中,这些 <iframe>
不会随宽高比进行缩放。
这正是 FitVids 所要解决的问题。 它会找到页面上的视频,找出它们的独特宽高比,然后将这些相同的 CSS 概念应用于它们,使它们能够流体宽度,同时保持其独特的宽高比。
FitVids 基于 jQuery,但也有原生 JavaScript 选项,例如 Ross Zurowski 的 这个。
但是……如果内容太多怎么办?
回到一会儿的任意(可能为文本)内容。
我们实际上是在这里设置高度,这在使用 CSS 时应该总是闪烁红色警示灯。 网页喜欢向下增长,设置固定高度是对这种自然运动的阻碍。
如果内容超过了空间,我们就进入了糟糕的设计领域

也许我们可以做一些事情,比如 overflow: auto;
,但这可能导致尴尬的设计领域。
伪元素策略
我认为,这已经成为处理具有完全任意内容的宽高比框的最佳方法。 Keith Grant 在他的 文章 中对此进行了很好的描述。 Marc Hinse 在 2013 年给出了 解释和演示。
使用这种技术,你可以获得宽高比框,而不需要太多标记,并且在内容超过高度时仍然安全。
诀窍是将百分比填充应用于伪元素,而不是框本身。 你将伪元素浮动,这样里面的内容就可以很好地放在里面,然后清除浮动。
.aspect-ratio-box {
background: white;
}
.aspect-ratio-box::before {
content: "";
width: 1px;
margin-left: -1px;
float: left;
height: 0;
padding-top: 591.44px / 1127.34px * 100%;
}
.aspect-ratio-box::after { /* to clear float */
content: "";
display: table;
clear: both;
}
看看它如何更加安全

还有一个视频
使用自定义属性
这可能是所有想法中最酷的!
Thierry Koblentz 最近写了一篇关于它的文章(删除链接,因为 Thierry 的网站已失效。),并感谢 Sérgio Gomes 的想法。
要使用它,你只需为需要它的元素设置一个作用域为该元素的自定义属性
<div style="--aspect-ratio:815/419;">
</div>
<div style="--aspect-ratio:16/9;">
</div>
<!-- even single value -->
<div style="--aspect-ratio:1.4;">
</div>
为它设置样式的 CSS 简直是天才之作
[style*="--aspect-ratio"] > :first-child {
width: 100%;
}
[style*="--aspect-ratio"] > img {
height: auto;
}
@supports (--custom:property) {
[style*="--aspect-ratio"] {
position: relative;
}
[style*="--aspect-ratio"]::before {
content: "";
display: block;
padding-bottom: calc(100% / (var(--aspect-ratio)));
}
[style*="--aspect-ratio"] > :first-child {
position: absolute;
top: 0;
left: 0;
height: 100%;
}
请允许我引用 Thierry 的分步解释
- 我们使用
[style*="--aspect-ratio"]
作为钩子来定位相应的框 - 无论是否支持自定义属性,我们都会拉伸内部框
- 我们确保图像的高度来自其内在比例,而不是其
height
属性 - 我们将容器设置为一个包含块(这样内部框会参考该祖先进行定位)
- 我们创建一个伪元素来用于“填充黑客技术”(正是这个元素创建了宽高比)
- 我们使用
calc()
和var()
根据自定义属性的值计算填充 - 我们将内部框的样式设置为与其包含块的尺寸相匹配
其他想法和工具
Lisi Linhart 向我推荐 了 Ratio Buddy,非常酷

请注意,它使用了伪元素,但仍然绝对定位了内部容器。这有点奇怪。你可能要么跳过伪元素并将填充直接放在容器上,要么使伪元素浮动,这样你就不用那个内部容器了。不过,我喜欢这种方法的简单易用。
Tommy Hodgins 有 CSSplus ,其中包含 Aspecty ,它专门用于此目的,假设你喜欢使用 JavaScript 解析并更改你的 CSS 的方法。
事实上,这些年来我见过很多现实世界中使用纵横比框的例子。如果你有使用经验,请分享一下!
我认为,如果我们可以在
calc
中使用attr
作为number
,我们可以使用类似下面的代码:height: calc(attr(width)/calc(height) * ...)
然后你就不得不使用每个元素上的旧的宽度和高度属性了。
我喜欢这个想法。是的,使用高度/宽度现在有点过时了,但这使得它完全自动化,适用于像粘贴的 YouTube 嵌入这样的东西,用户不需要任何操作(在 CMS 的情况下)。
这真是一个 CSS 技巧。
我不得不怀疑这是否会成为浏览器最终会提供的一个合适解决方案。就像以前使用
float:right
来使某个元素右对齐以使其与流体内容并排,而现在 flexbox 已经使得这种做法变得不必要了。使用自定义属性很好,我唯一担心的是,像
[style*="--aspect-ratio"]
这样的选择器对我来说似乎有点“脆弱”。必须严格遵守某种命名约定,否则像style="--aspect-ratio-alt: 1.5"
这样的属性就会触发声明。顺便说一句,只有我一个人看不到大多数视频吗?我在控制台中有四个“无法加载资源:net::ERR_SPDY_PROTOCOL_ERROR”错误(Mac 上的 Chrome 60 – 还有 Android 上的 Chrome 58)。
公平地说,但要指出这种方法的额外优势,
style="--aspect-ratio-alt: 1.5"
仍然有效!“padding-top 和 padding-bottom 是基于元素的宽度。”
我想把它改成“基于父元素的宽度”。这是一个很小的区别,但我第一次尝试弄懂的时候花了很长时间才理解。你不能仅仅创建一个宽度为 500px 的 div,将其设置为零高度,然后给它加上 100% 的填充,并得到那个 500px 的红色框。它必须从一开始就放在一个宽度为 500px 的 div 中!
既然我们说到这里,我认为这是最奇怪的 CSS 行为。我个人并不理解这是怎么发生的……但我感谢它能工作!我在几乎所有项目中都使用它。
而它基于父元素的事实是为什么
一点也不奇怪。
将填充放在伪元素上实际上使得高度比率依赖于元素本身的宽度,而不是依赖于元素容器的宽度。
将填充放在元素本身,当给元素一个不为 100% 的明确宽度时,需要对填充设置进行微调。
感谢你的澄清。我发现你几乎在所有项目中都使用它很有趣。我在一个项目中使用了它,是的,这是 CSS 最奇怪的方面之一。
上面提到的“伪元素”技术的另一个变体是使用flexbox 容器,而不是block > float-and–clear 模式,如所示,因为 flex 行兄弟元素将始终与行中最高的元素一样高。标记和样式更简单
flexbox 最大纵横比 2:1
对传统技术的良好总结或“CSS 老式” ;-)
但是今天,我们已经可以做到这一点,而不需要计算填充,也不需要声明“position: absolute”,也不需要使用变量或其他方法来维护纵横比。
所有这些都可以由 CSS 网格布局来完成,因为它可以将多个元素放置在同一个网格区域中,而不会离开文档流。
一个示例笔
https://codepen.io/Kseso/full/VWaWZR/
一篇帖子(抱歉,它在我的博客上)包含更多示例和西班牙语解释。
在我工作的地方,我们的内容制作人员要求所有图像都始终为 16:9 – 当我们也构建可定制的、响应式和移动友好的网站时,这是一个具有挑战性的要求。
我们的解决方案是在最常见的浏览器尺寸下支持接近 16:9 的尺寸,并与我们的客户合作,解释不始终将内容锁定在特定比率的益处。如果他们仍然想要这样做,我们会使用 background-size/object-fit: contain 作为权宜之计。我们不应该为了一个比率而构建整个平台。
相关的最新文章
https://www.bram.us/2017/06/16/aspect-ratios-in-css-are-a-hack/
很棒的伪元素比率提示!但是,如何水平居中文字呢?(将浮动技术应用于生日快乐演示)。
一个 flex 解决方案会破坏 % 填充,absolute 不会扩大盒子高度……
Geoff Yuen 写道
并指向 Stack Overflow 进行解释:https://stackoverflow.com/questions/36783190/why-doesnt-percentage-padding-margin-work-on-flex-items-in-firefox-and-edge/36783414#36783414