β

CSS实现长宽比的几种方案

W3CPlus 716 阅读
css

在上一篇文章中总结了CSS如何实现 容器长宽比 几种方法。对于在CSS中实现容器的长宽比最初的创意是来自于在 Web中如何实现纵横比 。主要是用于响应式设计中的 iframe img video 之类的元素。随之扩展为适用于任何的容器长宽比。那在CSS中到底有多少种方案可以实现呢?我一向喜欢做这样的事情,到处搜集多种解决方案。

如果你看到前面的两篇文章,或许你知道一些方法,或者说所有的方案你都想到了。如果是这样的话,你可以停止阅读这篇。如果没有的话,你可以继续往下阅读。

长宽比故事

长宽比 在影视制作中又被称之为 宽高比 ,指的是一个视频的宽度除以它的高度所得到的比例,通常表示为 x:y x×y ,其中的冒号和叉号表示中文的“比”之意。目前,在电影工业中最常被使用的是anamorphic比例(即 2.39:1 )。传统的 4:3 仍然被使用于现今的许多电视画面上,而它成功的后继规格 16:9 则被用于高清晰度电视或数字电视上。常见的比例:

有关于长宽比更多的介绍可以阅读Wiki上的 Aspect ration 一文。

HTML结构

使用CSS实现容器长宽比,常见的HTML模板结构有两种:

<div class="aspectration" data-ratio="16:9">
    <div class="content"></div>
</div>

另外一种结构是:

<div class="aspectration" data-ratio="16:9">
</div>

具体使用的时候,根据自己使用场景采用不同的结构。

CSS实现长宽比例方案

前面也提到过了,使用CSS实现长宽比方案有多种,下面简单的罗列一下这些方案。不过每种方案都不会详细介绍,因为代码非常简单,看一眼代码就能明白其中原理。

垂直方向的padding

这是最早提出的一种实现方案,主要借助的原理是利用 padding-top 或者 padding-bottom 的百分比值,实现容器长宽比。在CSS中 padding-top padding-bottom 的百分比值是根据容器的 width 来计算的。如此一来就很好的实现了容器的长宽比。采用这种方法,需要把容器的 height 设置为 0 。而容器内容的所有元素都需要采用 position:absolute ,不然子元素内容都将被 padding 挤出容器(造成内容溢出)。

比如我们容器的长宽比是 16:9 ,那么根据计算: 100% * 9 / 16 可以得到 56.25% 。如果你希望的是 4:3 ,那么对应的就是 100% * 3 / 4

具体的CSS代码如下:

.aspectration {
    position: relative; /*因为容器所有子元素需要绝对定位*/
    height: 0; /*容器高度是由padding来控制,盒模型原理告诉你一切*/
    width: 100%; 
}
.aspectration[data-ratio="16:9"] {
    padding-top: 56.25%;
}
.aspectration[data-ratio="4:3"]{
    padding-top: 75%;
}

通过通配符 * 选择器,让其子元素的宽高和容器 .aspectration 一样大小:

.aspectration > * {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
}

padding & calc()

这个方案采用的是 padding calc() 配合在一起使用。其实原理和第一个方案是一样的。只不过在第一个方案中,我们每次都需要对 padding 的值计算,如果使用 calc() 可以通过浏览器直接计算出 padding 的百分比值。

.aspectration[data-ratio="16:9"] {
    padding-top: calc(100% * 9 / 16);
}

padding & CSS变量

对于变量而言,以前是其他计算器语言和CSS处理器的特性,不过值得特性的是,现在也是CSS的特性。接下来的这个方案也是基于 padding 原理,只不过是借助于CSS变量特性,让前面的方案变得更为灵活一些。使用CSS的变量时,可以把HTML中 data-ratio 去掉了。换成 style="--aspect-ratio:16/9" ,也可以是 style="--aspect-ratio:1.4;" 。同时也可以借助于第二个方案中的 calc() 。因为CSS的变量和 calc() 函数的结合是一种完美的结合。

.aspectration[style*="--aspect-ratio"] {
    padding-top: calc(100% / (var(--aspect-ratio)));
}

padding & 伪元素

前面的方案都是在 .aspectration 元素上使用 padding 值。但在CSS中,还可以使用CSS的伪元素 ::before ::after 来撑开容器。

.aspectration {
    position: relative;
}
.aspectration:after {
    content: "";
    display: block;
    width: 1px;
    margin-left: -1px;
    background-color: orange;
}
.aspectration[data-ratio="16:9"]:after {
    padding-top: 56.25%;
}
.content {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
}

视窗单位

CSS新特性中提供了一种新的单位 vw 。了解过这个单位的同学都知道,浏览器 100vw 表示的就是浏览器的视窗宽度(Viewport)。打个比方说,如果你的浏览器是 1334px ,那么对就的 100vw = 1334px 。这个时候也就是说 1vw = 13.34px 。这里的 100vw 也对应前面方案中的 100% 。这样我们就可以把前面的 % 单位换成 vw 的单位。打个比方说, 16:9 对应的就是 100vw * 9 / 16 = 56.25vw 。这个值可以用在 padding-top 或者 padding-bottom 中。但这里演示的不再是 padding 了,而是把这个值给 height

.aspectration[data-ratio="16:9"] {
    width: 100vw;
    height: 56.25vw;
}

上面的示例中 width 的值是 30vw ,那么对应的 height 值就是 30vw * 9 / 16 = 16.875vw

视窗单位 & CSS Grid

这是一个很有创意的解决方案,使用的都是CSS新特性:视窗单位和CSS Grid Layout。简单说一下其中的实现原理:将容器 .aspectration 通过 display:grid 声明为一个网格容器,并且利用 repeat() 将容器划分为横向比例,比如 16 ,那么每一格的宽度对应的就是 100vw * 9 / 16 = 6.25vw 。同样使用 grid-auto-rows ,将其设置的值和横向的值一样。在子元素上通过 grid-column grid-row 按比例合并单元格。

.aspectration {
    display: grid;
    grid-template-columns: repeat(16, 6.25vw);
    grid-auto-rows: 6.25vw;
}
.aspectration[data-ratio="16:9"] .content {
    grid-column: span 16;
    grid-row: span 9;
}

未来原生方案 aspect-ratio

WICG的讨论 上,有人提出来了原生的长宽比属性 aspect-ratio 。例如,给定一个容器元素它的 width height 都设置为 auto ,并且 aspect-ratio 的值为 2/1 max-height:200px 。一个容器宽度为 500px 时,元素首先会设置 width:500px ,然后根据 aspect-ratio 比例将 height 设置为 250px 。这个时候其实违反了 max-height 的约束。相反,容器大小会变成 height: 200px width:400px 。另外,如果元素的 max-width 450px 时,长宽比将会完全被忽视,因为无法满足。

如果把 width 设置为一个百分比,高度不设置一个固定值:

.aspectration[data-ratio="16:9"] {
    width: 100%;
    height: aspect-ratio(16/9);
}

现在讨论讨论趋势是把 aspect-ratio 属性值变成属性,比如:

.aspectration[data-ratio="16:9"] {
    width: 100%;
    aspect-ratio: calc(16/9);
}

到目前为止,还没有任何浏览器支持该属性,但我们可以借助 PostCSS Aspect Ratio 插件来实现上述的功能:

/* Input. */ 
.aspect-box {
    position: relative;
    aspect-ratio: '16:9';
}
/* Output. */
.aspect-box {
    position: relative;
    box-sizing: border-box;
} 
.aspect-box > * /* This targets .aspect-box__content */ {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    box-sizing: border-box;
} 
.aspect-box:before /* This pseudo element uses the padding trick to set the height. */ { 
    position: relative;
    display: block; 
    content: "";
    padding-top: 56.25%;
    box-sizing: border-box;
}

总结

文章总结了几种用来实现长宽比的方案。有兴趣的同学可以自己体验一下,当然你也许你还有其他更优秀的方案,欢迎在下面的评论中与我们一起分享。

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《 图解CSS3:核心技术与案例实战 》。

如需转载,烦请注明出处: https://www.w3cplus.com/css/aspect-ratio.html

css
作者:W3CPlus
原文地址:CSS实现长宽比的几种方案, 感谢原作者分享。