一、响应式布局概念

Responsive design 能实现不同屏幕分辨率的终端上浏览网页展示不同样的样式。使网站在手机和平板电脑上有更好的浏览阅读体验
image.png

二、响应式设计的步骤

1. 设置 Meta 标签

大多数移动浏览器将HTML 页面视图(viewport) 的宽为设备宽度,用来适应屏幕分辨率
你可以使用视图的 meta标签 来进行重置

  1. <!-- 按快捷键 meta:vp+tab 可以快速生成 -->
  2. <head>
  3. <meta charset="UTF-8">
  4. <!-- 在<head>标签里加入这个meta标签 -->
  5. <!-- 下面的视图标签告诉浏览器,使用设备的宽度作为视图宽度并禁止初始的缩放 -->
  6. <meta name="viewport" content="width=device-width,
  7. initial-scale=1.0,
  8. minimum-scale=1.0
  9. maximum-scale=1.0,
  10. user-scalable=no">
  11. </head>

[1]user-scalable = no 属性能够解决 iPad 切换横屏之后触摸才能回到具体尺寸的问题

2. 通过媒介查询来设置样式 Media Queries

Media Queries 是响应式设计的核心

/* 它根据条件告诉浏览器如何为指定视图宽度渲染页面。假如一个终端的分辨率小于 980px */
@media screen and (max-width: 980px) {
  #head { … }
  #content { … }
  #footer { … }
}

2.1 设置多种试图宽度

/** iPad **/
@media only screen and (min-width: 768px) and (max-width: 1024px) {}

/** iPhone **/
@media only screen and (min-width: 320px) and (max-width: 767px) {}

2.2 智能手机(纵向和横向)视图

/* 智能手机(纵向和横向)Smartphones (portrait and landscape) */
@media only screen and (min-device-width : 320px) and (max-device-width : 480px) {

}

/* 智能手机(横向) */
@media only screen and (min-width : 321px) {

}

/* 智能手机(纵向)Smartphones (portrait) */
@media only screen and (max-width : 320px) {

}

3. 响应式图像

响应式设计的网页图像,就是 响应式图像 responsive image
Responsive 响应式布局 - 图2
响应式图像的解决方案有很多,JavaScript 和 CSS 都可以实现。本文介绍最简单的、语义性最好的 HTML 方法,浏览器原生支持

3.1 问题的由来

/* <img>标签用于插入网页图像,所有情况默认插入的都是同一张图像 */
<img src="foo.jpg">

上面代码在桌面端和手机上,插入的都是图像文件 foo.jpg,这种处理方法固然简单,但是有三大弊端:

  • 体积: 一般来说,桌面端显示的是大尺寸的图像,文件体积较大。手机的屏幕较小,只需要小尺寸的图像,可以节省带宽,加速网页渲染
  • 像素密度: 桌面显示器一般是单倍像素密度,而手机的显示屏往往是多倍像素密度,即多个像素合成为一个像素,称为 Retina屏幕。图像文件很可能在桌面端很清晰,放到手机上会有点模糊,因为像素扩充了
  • 视觉风格: 桌面显示器的面积较大,图像可以容纳更多细节。手机的屏幕较小,许多细节是看不清的,需要突出重点

image.png
上面两张图片,右边的手机图片经过裁剪以后,更突出图像重点,明显效果更好

视网膜屏幕(retina屏幕) 清晰度解决方案
视网膜屏幕指的是屏幕的物理像素密度更高的屏幕
物理像素可以理解为屏幕上的一个发光点,无数发光的点组成的屏幕
视网膜屏幕比一般屏幕的物理像素点更小,常见有2倍和3倍的视网膜屏幕

  • 2倍视网膜屏幕: 物理像素点大小是一般屏幕的1/4
  • 3倍视网膜屏幕: 物理像素点大小是一般屏幕的1/9

retina屏幕.pngretina屏幕2.png

3.2 像素密度的选择:srcset属性

window.devicePixelRatio 查看像素分辨比

为了解决上一节的这些问题,HTML 语言提供了一套完整的解决方案。首先,<img>标签 引入了srcset属性
srcset属性 用来指定多张图像,适应不同像素密度的屏幕。它的值是一个逗号分隔的字符串,每个部分都是一张图像的 URL,后面接一个空格,然后是像素密度的描述符。请看下面的例子

<img srcset="foo-320w.jpg,
             foo-480w.jpg 1.5x,
             foo-640w.jpg 2x"
     src="foo-640w.jpg">
  • srcset属性 给出了三个图像 URL,适应三种不同的像素密度
  • 图像 URL 后面的像素密度描述符,格式是 像素密度倍数 + 字母x
    • 1x表示单倍像素密度,可以省略
    • 浏览器根据当前设备的像素密度,选择需要加载的图像
  • 如果 srcset属性 都不满足条件,那么就加载 src属性 指定的默认图像

3.3. 图像大小的选择:srcset属性 + sizes属性

像素密度的适配,只适合显示区域一样大小的图像。如果希望不同尺寸的屏幕,显示不同大小的图像,srcset属性就不够用了,必须搭配sizes属性

<img srcset="foo-160.jpg 160w,
             foo-320.jpg 320w,
             foo-640.jpg 640w,
             foo-1280.jpg 1280w"
     sizes="(max-width: 440px) 100vw,
            (max-width: 900px) 33vw,
            254px"
     src="foo-1280.jpg">
  • 第一步: srcset属性列出所有可用的图像
    • 上面代码中,srcset属性 列出四张可用的图像,每张图像的 URL 后面是一个空格,再加上宽度描述符
    • 宽度描述符就是图像原始的宽度,加上字符w。上例的四种图片的原始宽度分别为160像素、320像素、640像素和1280像素
  • 第二步: sizes属性列出不同设备的图像显示宽度
    • sizes属性 的值是一个逗号分隔的字符串,除了最后一部分,前面每个部分都是一个放在括号里面的媒体查询表达式,后面是一个空格,再加上图像的显示宽度
    1. 上面代码中,sizes属性给出了三种屏幕条件,以及对应的图像显示宽度。宽度不超过440像素的设备,图像显示宽度为100%;宽度441像素到900像素的设备,图像显示宽度为33%;宽度900像素以上的设备,图像显示宽度为254px
  • 第三步: 浏览器根据当前设备设置的宽度, 在sizes属性获得图像的显示宽度,然后从srcset属性找出最接近该宽度的图像,进行加载
    • 假定当前设备的屏幕宽度是480px,浏览器从sizes属性查询得到,图片的显示宽度是33vw(即33%),等于160px。srcset属性里面,正好有宽度等于160px的图片,于是加载foo-160.jpg。

⚠️注意: sizes属性必须与srcset属性搭配使用。单独使用sizes属性是无效的

3.4 标签,标签

上面两节分别解决了像素密度和屏幕大小的适配,但是如果要同时适配不同像素密度、不同大小的屏幕,应该怎么办呢?
这时,就要用到 <picture>标签。它是一个容器标签,内部使用 <source>和<img>,指定不同情况下加载的图像

<!-- 设备宽度如果不超过500px,就加载竖屏的图像,否则加载横屏的图像 -->
<picture>
  <source media="(max-width: 500px)" srcset="cat-vertical.jpg">
  <source media="(min-width: 501px)" srcset="cat-horizontal.jpg">
  <img src="cat.jpg" alt="cat">
</picture>

上面代码中,标签内部有两个标签和一个Responsive 响应式布局 - 图6标签

  • <source>标签 的 media属性 给出媒体查询表达式,srcset属性就是 <img>标签 的srcset属性,给出加载的图像文件。sizes属性其实这里也可以用,但由于有了media属性,就没有必要了。
  • 浏览器按照 <source>标签 出现的顺序,依次判断当前设备是否满足media属性的媒体查询表达式,如果满足就加载srcset属性指定的图片文件,并且不再执行后面的<source>标签和 <img>标签
  • <img>标签 是默认情况下加载的图像,用来满足上面所有 <source>标签 都不匹配的情况

下面给出一个例子,同时考虑屏幕尺寸和像素密度的适配

<picture>
  <source srcset="homepage-person@desktop.png,
                  homepage-person@desktop-2x.png 2x"       
          media="(min-width: 990px)">
  <source srcset="homepage-person@tablet.png,
                  homepage-person@tablet-2x.png 2x" 
          media="(min-width: 750px)">

  <img srcset="homepage-person@mobile.png,
               homepage-person@mobile-2x.png 2x" 
       alt="Shopify Merchant, Corrine Anestopoulos">
</picture>

上面代码中,标签 的 media属性 给出屏幕尺寸的适配条件,每个条件都用srcset属性,再给出两种像素密度的图像 URL

3.5 标签的type属性

除了响应式图像 <source>标签 还可以用来选择不同格式的图像。比如,如果当前浏览器支持 Webp 格式,就加载这种格式的图像,否则加载 PNG 图像

<picture>
  <source type="image/svg+xml" srcset="logo.xml">
  <source type="image/webp" srcset="logo.webp"> 
  <img src="logo.png" alt="ACME Corp">
</picture>

<source>标签 的type属性给出图像的 MIME 类型,srcset是对应的图像 URL

  • 浏览器按照 标签 出现的顺序,依次检查是否支持type属性指定的图像格式,如果支持就加载图像,并且不再检查后面的 标签
  • 图像加载优先顺序依次为 svg 格式、webp 格式和 png 格式

3.6 处理图片缩放的方法

  • 宽度设置为 auto %
  • 设置最小最大宽度 min-width max-width
    img { 
    width: auto; 
    max-width: 100%; 
    }
    

3.7 使用 content 属性

::before+::after+content 属性来动态显示一些内容或者做其它很酷的事情,在 CSS3 中,任何元素都可以使用 content 属性了

<img src="image.jpg" 
     data-src-600px="image-600px.jpg" 
         data-src-800px="image-800px.jpg" alt="">

<style>
  @media (min-device-width:600px) {
    img[data-src-600px] {
      content: attr(data-src-600px, url);
    }
  }

  @media (min-device-width:800px) {
    img[data-src-800px] {
      content: attr(data-src-800px, url);
    }
  }
</style>
<style>
  .box:after {
    content: "我是一个使用*content*属性生产的静态文字";
  }

  div[data-line]:after {
    content: "[line "attr(data-line) "]";
  }
</style>

<div class="box" data-line="1" onclick="clickMethod(this)"></div>

<script>
  var clickMethod = function (e) {
    console.log(e,e.getAttributeNames(), document.querySelector(".box").getAttributeNames());
  }
</script>

4. 其他属性

例如 pre ,iframe,video 等,都需要和img一样控制好宽度。对于table,建议不要增加 padding 属性,低分辨率下使用内容居中:

table th, table td { padding: 0 0; text-align: center; }