• 欢迎访问web前端中文站,JavaScript,CSS3,HTML5,web前端demo
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏web前端中文站吧

容器长宽比

CSS3 web前端中文站 2年前 (2017-06-26) 1256次浏览 已收录 2个评论

CSS3

容器长宽比,这个话题在站上也有相关的文章介绍,最早出现于 Responsive Web Design 中,主要用来处理imgiframevideoobject这些元素的自适应问题。简单点讲,就是根据容器的宽度,按照宽高比例自动计算出容器的大小。并且让图片,视频之类能自适应容器。另外记得在知乎上有一个问题“移动端布局,div按比例布局,宽度为百分比,但又想让高度和宽度一样,即让div为正方形,怎么做布局呢?”,其实解决方案在前面的教程已提到过:

更多精彩内容请看 web 前端中文站
http://www.lisa33xiaoq.net 可按 Ctrl + D 进行收藏

  • Flexible Images
  • Web 中内嵌内容实现响应式效果
  • Web 中如何实现纵横比

既然有相应的解决方案,继续花时间来说,是不是有点多余。那么这个问题又回到了 CSS 的根源上:在 Web 中,使用 CSS 解决问题,往往不只有一种方案,只有更适合的方案。

这两天看到@Chris Coyier 特意也整理了一篇《Aspect Ratio Boxes》文章,里面有新的方案值得我们思考,特别是 CSS 自定义属性的部分。那我们再次花时间将相关方案整理在一起,仅供学习与参考。

方案一:基于宽度的百分比

首先介绍的方案是基于容器widthpadding一个百分比。这也是最早的一个方案。主要的原理是基于元素的padding-toppadding-bottom是根据元素的width进行计算的。假设你有一个div容器,它的宽度是500px,你想让其高度也是和宽度一样,也就是说宽高比例是1:1。这个时候借助padding-top或者padding-bottom的值为100%,就可以计算出容器div的高度是500px

这种方案有一个必要条件,容器divheight0,同时box-sizingborder-box,不然的话,容器不能带有border。现在我们可以想象一下,如果容器div的宽度又是一个百分比值,这样一来就可以保持容器高度跟宽度始一致。另外再想象一下,如果我们的padding-bottompadding-top不是100%,而是56.25%,其实这就是一个完美的宽高比16:9,也就是9 / 16 * 100% = 56.25%。如此一来,你可以根据你自己的设计比来进行调整。

而这样的场景仅适合容器中放置背景图片:

<div class="aspect-ratio-boxes"></div>  .aspect-ratio-boxes{     overflow: hidden;     height: 0;     padding-top: 56.25%;     background: url(/images/happy-birthday.svg); } 

注:如果背景图片不是 SVG 文件,那还需借助于background-size或者object-fit来处理。比如设置为cover这样的值。

回到我们的问题中来,很多时候,我们的图片比例并不是和容器比例一样,比如在这个示例中,有可能不是16:9。就算是一个 SVG,假设这个 SVG 的viewBox = "0 0 1127.34 591.44",这也意味着它本质上是一个1127.34×591.44图像,它的比例是1127.34:591.44。或者说它也有可能是一个328×791的图形。

我想这种现象应该是很常见的,一个随机图像不一定符合预期的长宽比。那么问题就来了,对于一个任何长宽比,我们将如何实现?

任何可能的长宽比计算

对于固定的长宽比,比如16:9,它是完美的,我们也无需头痛。但事实上,往往并不如此,总是充满了随机性,这样也就表示比例也是随机性的。如果我们无法掌握随机性的比例,那么就会造成一个图像或视频被裁剪或者被拉伸(也有可能是被挤压)。这样对于追求完美的同学而言,是无法接受的。

那么需要自问一下,有没有办法通过 CSS 的计算,实现任何可能的长宽比计算呢?

值得庆幸的是可以的。在 CSS 中有一个神奇的calc()函数,它可以做一些基本的数学计算。拿上面的示例来说,SVG 的长宽是1127.34×591.44,可以通过calc()计算出padding-top或者padding-bottom的值:

padding-top: calc(591.44 / 1127.34 * 100%); 

这个时候可能有同学会提,使用calc()在客户端进行计算,会不会有性能问题?事实会不会呢?其实我回答不了,因为我没有进行过这方面的论证,同时也没有看到相关论证的文章。如果你对这方面感兴趣的话,不仿论证一下。除此之外,我的小伙伴说,任何性能问题随着硬件的发展都不会是问题。既然如此,我们忽略所谓的性能问题,回到这里。我们实际中使用calc()时,记得把单位带上,就上面的示例而言,我们的图片单位是px,此时在 Sass 这样的处理器中,可以直接这样计算:

padding-top: 591.44px / 1127.34px * 100%; 

实际使用就是这么的简单。calc()都可以不使用了。

如果你对 CSS 有所了解的话,上面这样使用,很有可能是仅仅适合用于背景图像,如果我们的容器用不了背景图像。也就是说,容器里放置的是一个img或者说是一个iframevideo。那就会产生一个新问题。

如果有内容,那么容器设置了padding-top会把容器的内容往下推;如果设置的是padding-bottom,会把内容往上推。

接下来我们需要解决的就是这个问题。

如何在设置了 padding 的时候不把内容往下(上)推

就上面的示例而言,如果在div容器设置了padding-top(或者padding-bottom)会造成容器的内容往盒子外推,此时设置了overflow:hidden时,溢出的内容就会看不见了。为了解决这个问题,首先会想到的是position:absolute

假设我们有这样的一个示例:

<div class="aspect-ratio-box">     <iframe class="aspect-ratio-box-inside" src="https://www.youtube.com/embed/upPCohrJcbw?showinfo=0&modestbranding=1" frameborder="0" allowfullscreen></iframe> </div> 

使用对应的 CSS:

.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%; } 

这是对于imgvideoiframe或者object的一个较好的解决方案。但如果我们容器.aspect-ratio-box-inside并不是这些元素,而是一些文本内容呢?我想你肯定会想到,将会出现什么问题?

容器长宽比

很显然溢出容器的内容被裁切掉了。这个时候较好的解决方案就是把overflow:hidden换成overflow:auto。但也是美中不足,会出现滚动条。既然如此,有没有较好的解决方案呢?先不回答,咱们先来尝试下面这样的一种方案:借助 CSS 的伪元素来做容器的宽高比例

.aspect-ratio-box {     background: white; } .aspect-ratio-box::before {     content: "";     width: 1px;     margin-left: -1px;     float: left;     height: 0;     padding-top: calc(591.44px / 1127.34px * 100%); } .aspect-ratio-box::after { /* to clear float */     content: "";     display: table;     clear: both; } 

来看一个示例效果吧:

从效果中可以告诉我们,这样处理方式,如果内容不超出容器的时候,容器的大小还具有对应的宽高比,如果内容超出容器的时候,会扩展容器的高度,让内容能足已展示。这样处理是不是更完美一些。

事实上,前面介绍的这些方法,我们在以前的文章中都有介绍过。但这篇文章具有营养之处是下一节内容。前面花这么多篇幅主要是让大家对宽高比具有一个更形象的理解。那就进入下一节吧。

使用 CSS 自定义属性

至于什么是 CSS 自定义属性,这里不做过多的介绍,如果从示接触这方面的内容,可以点击这里进行了解。接下来的内容,我假装你了解了 CSS 的自定义属性。首先在:root里声明一个全局变量:

:root {     --aspect-ratio: 1 / 1; } 

在实际使用的时候,可以借助 CSS 自定义属性的局部变量来覆盖全局变量。比如:

<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"] > * {       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%;     }   } 

这个时候,如果借助vw这样的单位,在容器上做一定的处理:

[style*="--aspect-ratio"]{     width: 50vw;     margin: 20px auto;     background:orange; } 

你将看到一个完美的效果:

改变浏览器视窗大小,你可以看到如下的一个效果:

容器长宽比

想出这样的 CSS 处理方式的人是不是天才呀。这样的思路值得我们去思考。上面的示例演示的是iframe的方式,事实上,上面的方式适合imgvideo之类的。如果回到前面的示例,如果是文本内容呢?我想我不多说,你也能找出类似的解决方案。当然,你可能会好奇上面的 CSS 代码是什么意思?对于 CSS 的老司机而言,上面的代码不是问题,对于新同学而言,你只要理解了 CSS 的属性选择器、CSS 的伪元素、CSS 自定义属性以及 CSS 的calc()@supports就可以很好的理解了。

其他的解决方案和思路

CSS 自定义属性的方案已经让我们感觉眼前一亮了。但除了上面介绍的一些方案,还有其他的解决方案,比如 CSSplus,他就提供了一个 Aspecty 特性,可以在你的代码中直接使用:

div {     background: lime;     --aspect-ratio: 478/239; } 

这种方法需要引入一个 JS 文件。这是其中不足之处。除此之外,@sgomes 写了一个 CSS-aspect-ratio 的 CSS 文件。你只需要将这个文件引入到你的项目中,你也可以很好的处理宽高比。比如

npm

npm i --save-dev css-aspect-ratio 

或者在你的 CSS 文件中:

@import https://unpkg.com/css-aspect-ratio@1/css-aspect-ratio.css; 

当然,你也可以将这个文件保存到你的本地,更疯狂的是,直接把这里面的代码 Copy 到你的 CSS 中。有了这个前提,你在项目中这样使用即可:

<div class="aspect-ratio" style="width: 768px; --aspect-ratio-w: 4; --aspect-ratio-h: 3;">     <img src="kitten.jpg" alt="A cute kitten"> </div> 

另外还要一个就是 PostCSS 的插件:PostCSS Aspect Ratio。如果你的构建工具中已经使用了 PostCSS,通过下面的命令就可以将这个插件安装到你的构建工具中:

npm install postcss-aspect-ratio --save 

比如你要使用的宽高比例是16:9,那可以这样使用:

HTML

<div class="aspect-box">     <div class="aspect-box__content">         <!-- Any content you like, very useful for video and image elements. -->     </div> </div> 

CSS

/* Input. */ .aspect-box {     position: relative;     background: lime;     aspect-ratio: '16:9'; }  /* Output. */ .aspect-box {     position: relative;     background: lime;     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; } 

如果你想在一定的比例上稍做调整,比如在4:3的比例上,容器高度少个20px,可以这样使用:

/* Input. */ .aspect-box {     position: relative;     background: lime;     aspect-ratio: calc('4:3' - 20px); }  /* Output. */ .aspect-box {     position: relative;     background: lime;     box-sizing: border-box; }  .aspect-box > * {     position: absolute;     top: 0;     right: 0;     bottom: 0;      left: 0;      box-sizing: border-box; }  .aspect-box:before {     position: relative;     display: block;     content: "";     padding-top: calc(75% - 20px);     box-sizing: border-box; } 

是不是感觉很 NB。如果你使用了,你也就变得 NB 了。不仿试试看。

总结

这是我见过,也是实战过的容器宽高比例的实现方案。不管是哪种方案,其基本原理都是不变的,和最原始的方案一样,借助于padding-top或者padding-bottom来实现。最简单的原理,容器的padding-top(或padding-bottom)的百分比是根据容器的width来计算的。唯有不同之处是,实现的手段不一样,那是因为我们 CSS 技术发展的变化。或者说开发者特别聪明,除了原生的开发,还可以借助一些工具,比如说 JS 插件,PostCSS 插件之类的。最后再提一下,如果我们容器自身是一个百分比单位,或者说是一个视窗单位,比如示例中用的vw。那么我们就能更好的实现一些自适应的布局。特别是在响应式设计当中,就一两年前,要实现这样的效果还是很痛苦的。

最近也在思考一种更适合移动端的布局方案,其中思路就是借助于这篇文章的相关知识,目前正在实测阶段,如果通过测试之后,我们就可以告别以前所使用的 REM 方案(也就是《使用 Flexible 实现手淘 H5 页面的终端适配》分享的方案)。如果你感兴趣,欢迎持续关注相关更新。如果你对宽高比有其他的经验,欢迎与我们一起分享。

【注:本文源自网络文章资源,由站长整理发布】

参考文档

  • Aspect Ratio Boxes By @CHRIS COYIER
容器长宽比

大漠

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

web 前端中文站 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:容器长宽比
喜欢 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(2)个小伙伴在吐槽
  1. 在我看来,如果自适应的话,要么百分比,要么使用现成的框架吧
    BanYuner2017-07-01 11:04 回复