Flexible Images

编辑推荐: 掘金是一个高质量的技术社区,从 CSS 到 Vue.js,性能优化到开源类库,让你不错过前端开发的每一个技术干货。 点击链接查看最新前端内容,或到各大应用市场搜索「 掘金」下载APP,技术干货尽在掌握中。

玩过响应式设计的同学或多或少都会在一些媒体上纠结,比如说图片,视频等自适应问题。而且有关于这方面的解决方案各有各的说法,似乎是没有一种方案是绝佳的,这样一来让人倍感头痛。这或许就是前端的烦恼吧。

我也一样,虽然整响应式设计蛮久的了,但对于图片方面的自适应处理还真没有找到一个好的方案,这回痛下决心,仔细找了一些相关教程阅读了一回。那么今天将整理一下,网络上有关于图片自适应(此处我称之为弹性图片Flexible Images)解决方案,希望这篇文章能帮助大家解决图片在响应式设计中给大家带来的烦恼。

模板

假设我们有一个两栏自适应的布局,在主内容中应用了一个图片,其结构如下:

<div class="figure">
  <div class="inner">
    <img src="http://w3cplus-cdn2.u.qiniudn.com/sites/default/files/blogs/2014/1401/flexible-image.jpg" alt="" /> 
    <p class="figcaption">Lo, the robot walks</p>
  </div>
</div>

这是一个很常见的结构,其效果就是一张图片,图片下面有一个简单的文本描述。在整个效果中,针对figure内元素写了一点样式

//SCSS
.figure {
  float: right;
  margin: 0.5em 0;
  margin-left: 1.9672131%;  //12px/610px 
  width: 45.9016393%;  // 280px/610px

  .inner {
    border: 10px solid hsla(333,50%,60%,.8);
    border-radius: 10px;
  }
  img {
    vertical-align: top;
  }

  p {
    background-color: hsla(333,50%,60%,.8);
    padding: 10px 10px 0;
    color: #fff;
  }
}

从上面的代码中我们可以想像出我们要的效果,figure占整个容器.content宽度的"45.9016393%"也就是"280px/610px"。虽然整个布局是自适应布局,但图片依旧撑破容器:

Flexible Images

这并不是我们想要我效果,我们真正想要达到的效果应该是这样的,换句话说理想中要的将是下图的效果:

Flexible Images

那接下来,我们观注的就是,如何让我们的效果达到上图所示的效果。

方案一:max-width

在介绍响应式设计的文章中,为了解决图片的自适应问题,都会提到使用max-width。Richard Rutter设计首先提出使用max-width方案

img {
    max-width: 100%;
}

在上面的示例基础上,我们为img添加max-width值为“100%”:

Flexible Images

更重要的是,在现代浏览器中发展到可以自动调整图像的比例,可以根据容器的大小缩放或者放大图像,并且图像的宽高比保持不变。

max-width:100%除了可以应用于自适应元素容器上之外也可以应用于固定元素容器上。而且可以应用于视频和其他富媒体上也具有同等的效果。

img,
embed,
object,
video {
  max-width: 100%;
}

不幸的是,max-width属性在IE7以及其以下版本并无法支持。

如果你需要兼容这些浏览器,你要考虑使用Javascript或者IE滤镜来处理

另外一点,在一些浏览器中仅指定图片的宽度,可能会导致浏览器重新处理布局,调整页面的时间周期会增加两到三倍,虽然周期不到一毫秒,但是累积起来,尤其是页面上有很多个这样的元素的时候,还是或多或少会影响页面的性能。为了解决这个问题,可以显式的指定图片的height值为auto

img {
    max-width: 100%;
    height: auto;
}

方案二:background-image

在响应式设计中实现图片自适应另一种方案可以采用background-image。因为在CSS3有一个background-size属性可以让我们的背景图片适应容器大小。

先简单的来模拟一个效果,基于上例的基础上,我们将模板结构做一下调整:

<div class="figure">
  <div class="inner">
    <div class="image-wrapper"></div>
    <p class="figcaption">Lo, the robot walks</p>
  </div>
</div>

将图片变成背景图片应用于image-wrapper容器之上。

//SCSS
.figure {
  float: right;
  margin: 0.5em 0;
  margin-left: 1.9672131%;  //12px/610px 
  width: 45.9016393%;  // 280px/610

  .inner {
    border: 10px solid hsla(333,50%,60%,.8);
    border-radius: 10px;
  }
  .image-wrapper {
    width: 100%;
    background: url("http://w3cplus-cdn2.u.qiniudn.com/sites/default/files/blogs/2014/1401/flexible-image.jpg") no-repeat center;
    min-height: 100px;
    background-size: cover;
  }

  p {
    background-color: hsla(333,50%,60%,.8);
    padding: 10px 10px 0;
    color: #fff;
  }
}

可以清楚的看到,在.image-wrapper应用了背景图片,并且配合了background-size:cover一起使用:

.image-wrapper {
    width: 100%;
    background: url("http://w3cplus-cdn2.u.qiniudn.com/sites/default/files/blogs/2014/1401/flexible-image.jpg") no-repeat center;
    min-height: 100px;
    background-size: cover;
  }

不过这里让你为难的是,在.image-wrapper容器中没有任何内容,以至于无法撑开容器的高度,因此想正常的显示出图片,那必须给容器指一个准确的高度或一个最小高度,言外之意,我们宽度可以自适应,但比例不会根据图片的宽高比例来定,另外图片会进行截取,如示例所示:

Flexible Images

很显然,这样的效果不理想,不是我们所需要的效果。那有什么方法可以让背景图片根据自身的比例来自适应呢?

在响应式设计中,布局可以根据设备调整宽度。就算是用百分比调整宽度,也会自动按比例调整元素的高度。换句话说,其宽度比例保持不变来调整大小。如果我们要使背景图片达到同等的效果,我们就必须得知道如何保持任何HTML元素的纵横比例。

针对这个问题,Rolf Timmermans 写了一篇文章Responsive background images with fixed or fluid aspect ratios,文章中介绍了如何解决背景图像的固定和流体的纵横比例。接下来我们一起来看看Rolf Timmermans是如何实现的。

固定纵横比例

在保持背景图片的纵横比例,关键之处是是让背景图片垂直居中。而其中主要是基于宽度的百分比来设置内距padding的百分比值。这种技术早前在创建视频纵横比例一文中有介绍,而这种技术也适用于其他的任何元素。

Flexible Images

假设我们有一张700像素宽和467像素高的图像。当宽度改变时,我们需要维护其纵横向比例是“16:9”。下面有一个示例,我们使用像素为单位,当然也可以使用em为单位,但我们的结构和前面示例一样:

<div class="figure">
  <div class="inner">
    <div class="image-wrapper"></div>
    <p class="figcaption">Lo, the robot walks</p>
  </div>
</div>

一般情况之下,我们知道图片的尺寸大小,根据纵横比例,我们可以通过下面的公式计算出内距padding-toppadding-bottom的百分比值:

padding-top或padding-bottom = (背景图片高度 / 背景图片宽度) * 100%

根据这个公式,可以轻松计算出:

padding-top(或padding-bottom) = 467 / 700 = 0.667142857 = 66.7142857%

如此一来,我们可以将前面的示例调整为:

//SCSS
.figure {
  margin: .5em;
  //背景图像宽度必须宽度为700px
  max-width: 700px; //图片的宽度

  .inner {
    border: 10px solid hsla(333,50%,60%,.8);
    border-radius: 10px; 
  }

  .image-wrapper {
    background: url("http://w3cplus-cdn2.u.qiniudn.com/sites/default/files/blogs/2014/1401/flexible-image.jpg") no-repeat center;
    background-size: cover;
    padding-top:66.7142857%; // 467px / 700px = 0.667142857
  }

  p {
    background-color: hsla(333,50%,60%,.8);
    padding: 10px 10px 0;
    color: #fff;
  }
}

效果如下:

Flexible Images

自适应纵横比例

在固定纵横比例的基础之上,做进一步的调整。假设我们在宽屏的PC上显示大的图片,而在移动设备上,我们不想使用相同的纵横比或图像变得太小。当然我们也不想使用完全相同的高度或让图像变得太高。我们更希望当宽度变小时,其高度也变得更小。而我们也把这种称为流体纵横比例。

Flexible Images

这种效果我们可以给元素设置一个高度,来减少padding-top或者padding-bottom的百分比值。假设我们的大图尺寸是700像素宽度和267像素高,而我们决定显示的图片尺寸是在300像素宽度和167像素的高度。现在我们需要计算高度height和内距padding-toppadding-bottom的值:

Flexible Images

上图显示了两个维度之间的关系。斜线的坡度对应于内距padding-toppadding-bottom的属性值。开始高度的值代表元素的高度height的属性值。

我们可以根据上图所示的公式计算出背景图像纵横比例。基于固定纵横比例的示例之上,将上面的示例修改成流体纵横比例:

.figure {
  margin: .5em;
  //背景图像的宽度为700px
  max-width: 700px;

  .inner {
    border: 10px solid hsla(333,50%,60%,.8);
    border-radius: 10px; 
  }

  .image-wrapper {
    background: url("http://w3cplus-cdn2.u.qiniudn.com/sites/default/files/blogs/2014/1401/flexible-image.jpg") no-repeat center;
    background-size: cover;
    height: 92px;
    padding-top:25%;
  }

  p {
    background-color: hsla(333,50%,60%,.8);
    padding: 10px 10px 0;
    color: #fff;
  }
}

效果如下所示:

Flexible Images

注:由于示例图片尺寸比例不够标准,此处想要表达的意思是:假设原图的比例是4:1(比如背景图片尺寸是800px宽,200px高);自适应后比例为2:1,(背景图片尺寸变为300px的宽和150px的高)。换句话说就是从4:1比例的800200的图片变成比例为2:1的300150的图片。

特别声明:以上方法以及思咱来自于Rolf Timmermans的Responsive background images with fixed or fluid aspect ratios一文。

上面两种方案主要是根据背景图片的纵横比例来实现图像的自适应效果。但在NecolasFlexible CSS cover images一文中提供另一种以纵横比实现图像自适应的效果。

他的特征同样是依靠覆盖背景图像的纵横比例来实现,假设我们的比例为:

Flexible Images

覆盖背景图像的纵横比例必须得满足以下几个条件:

  • 呈现一个固定的纵横比例,除非特定的最大尺寸超过图像宽度
  • 支持不同的纵横比例
  • 支持最大高度max-height和最大宽度max-width
  • 支持不同的背景图像
  • 背景图像填充整个容器
  • 背景图像居中显示

先来看一个简单的模板:

<div class="CoverImage FlexEmbed FlexEmbed--3by1"></div>

对应的样式代码:

//SCSS
.CoverImage {
    border: 5px solid green;
    margin: .5em auto;
    background: url("http://w3cplus-cdn2.u.qiniudn.com/sites/default/files/blogs/2014/1401/flexible-image.jpg") no-repeat center;
    background-size: cover;
    max-height: 700px;
    max-width: 467px;
 }
 .FlexEmbed {
    display: block;
    overflow: hidden;
    position: relative;

    &:before {
      content: "";
      display: block;
      width: 100%;
    }
}


.FlexEmbed--3by1:before {
  padding-bottom: 33.33333%;
}

.FlexEmbed--2by1:before {
  padding-bottom: 50%;
}

.FlexEmbed--16by9:before {
  padding-bottom: 56.25%;
}

.FlexEmbed--4by3:before {
  padding-bottom: 75%;
}

其效果如下:

Flexible Images

使用背景图像的方案存在一个问题,不管是固定纵横比例还是流体纵横比例,我们都离不开background-size属性,而此值是CSS3的一个属性,仅有现代浏览器支持。如果要兼容低版本的IE还需要另寻他法。比如说backgroundSize.jsBACKSTRETCHjQuery Easy Background Resize等插件。Michael BesterThe Flexible Scalable Background Image, Redux一文中也做过这方面的介绍。

方案三:Object-fit

Object-fit是CSS3的一个新属性,到目前为止仅在Chrome32+版本上可以正常运行。这个属性可以说就是为自适图片尺寸而生,特别适合于响应式设计中图片以及其他富媒体的自适尺寸的处理。

使用Object-fit属性有一个受限条件,需要在样式中显示的设置图片的尺寸。然后通过其属性值fillcovercontain值来控制图像显录的纵横比例。在此并不建议使用,不过当作兴趣爱好,可以深入了解他。如果你对此属性感兴趣,可以阅读《CSS3 Object-fit和Object-position》一文,文中详细介绍了object-fit属性的具体使用。

其他方案

除了上述的几种方案之外,其实还有其他的一些解决方案,比如@张鑫旭在其博客《热门:响应图片(Responsive Images)技术简介》一文中就介绍了如何使用“Cookie+Server”、“使用noscript标签创建”等其他方法。Andy Shora在《Sizing Fluid Image Containers with a Little CSS Padding Hack》还介绍了一种老方案——内距和绝对定位实现图片自适应。当然除了这些还有其他的。Sherri Alexander特意为解决响应式设计的图片问题,在《Choosing A Responsive Image Solution》一文中搜集了很多个JavaScript解决方案。感兴趣的同学,可以仔细研究研究。

扩展阅读

总结

上面介绍了使用不同方案来解决图片自适应在Web页面设计中的问题。不管哪种方案,都有自己的优势与不足。抛开JavaScript的解决方案,不管是max-widthbackground-image还是object-fit都避免不了浏览器的兼容性问题,特别是object-fit尤为突出。在实际应用中,个人更趋向于方案二,因为其可以按照图片纵横向比例显示,不过这种方案比较麻烦的是,需要使用背景图片。

当然,大家可以根据自己的项目需求去考虑使用什么方案解决问题,在这里只是自己一点总结。如果您对自适应图片的处理有更好的解决经验,欢迎一起分享。

如需转载,烦请注明出处:http://www.w3cplus.com/css/flexible-images.html

返回顶部