内容简介:容器长宽比
容器长宽比,这个话题在站上也有相关的文章介绍,最早出现于Responsive Web Design中,主要用来处理 img
、 iframe
、 video
和 object
这些元素的自适应问题。简单点讲,就是根据容器的宽度,按照宽高比例自动计算出容器的大小。并且让图片,视频之类能自适应容器。另外记得在知乎上有一个问题“ 移动端布局, div
按比例布局,宽度为百分比,但又想让高度和宽度一样,即让 div
为正方形,怎么做布局呢? ”,其实解决方案在前面的教程已提到过:
既然有相应的解决方案,继续花时间来说,是不是有点多余。那么这个问题又回到了CSS的根源上:在Web中,使用CSS解决问题,往往不只有一种方案,只有更适合的方案。
这两天看到@Chris Coyier特意也整理了一篇《 Aspect Ratio Boxes 》文章,里面有新的方案值得我们思考,特别是CSS自定义属性的部分。那我们再次花时间将相关方案整理在一起,仅供学习与参考。
方案一:基于宽度的百分比
首先介绍的方案是基于容器 width
给 padding
一个百分比。这也是最早的一个方案。主要的原理是基于元素的 padding-top
或 padding-bottom
是根据元素的 width
进行计算的。假设你有一个 div
容器,它的宽度是 500px
,你想让其高度也是和宽度一样,也就是说宽高比例是 1:1
。这个时候借助 padding-top
或者 padding-bottom
的值为 100%
,就可以计算出容器 div
的高度是 500px
。
这种方案有一个必要条件,容器 div
的 height
为 0
,同时 box-sizing
为 border-box
,不然的话,容器不能带有 border
。现在我们可以想象一下,如果容器 div
的宽度又是一个百分比值,这样一来就可以保持容器高度跟宽度始一致。另外再想象一下,如果我们的 padding-bottom
或 padding-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
或者说是一个 iframe
、 video
。那就会产生一个新问题。
如果有内容,那么容器设置了 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%; }
这是对于 img
、 video
、 iframe
或者 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
的方式,事实上,上面的方式适合 img
、 video
之类的。如果回到前面的示例,如果是文本内容呢?我想我不多说,你也能找出类似的解决方案。当然,你可能会好奇上面的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页面的终端适配 》分享的方案)。如果你感兴趣,欢迎持续关注相关更新。如果你对宽高比有其他的经验,欢迎与我们一起分享。
参考文档
大漠
常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《 图解CSS3:核心技术与案例实战 》。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
无懈可击的Web设计
西德霍姆 / 刘建宁 / 清华大学出版社 / 2009-4 / 59.90元
一个网站,无论视觉上多么美观,内容多么丰富,如果不能面向最广泛的用户群,那它就不算是真正成功的网站。《无懈可击的Web设计:利用XHTML和CSS提高网站的灵活性与适应性》是Web标准设计领域的公认专家Dan Cederholm的倾力之作,向您描述了基于Web标准的设计策略,以适应各种各样的用户浏览方式。书中每一章的开头都给出了一个基于传统HTML技术的实例,然后对它进行重构,指出它的局限性,并利......一起来看看 《无懈可击的Web设计》 这本书的介绍吧!