响应式布局
(一)几个专有名词和单位
这里,我们先来辨析一下在适配的时候经常会遇到的一些名词、数值单位。
1.尺寸/像素/分辨率
首先,先来看一下物理像素。
以iphone6为例,可知道:
分辨率:1334pt x 750pt 指的是屏幕上垂直有1334个物理像素,水平有750个物理像素。
屏幕尺寸:4.7in 注意英寸是长度单位,不是面积单位。4.7英寸指的是屏幕对角线的长度,1英寸等于2.54cm。
屏幕像素密度:326ppi 指的是每英寸屏幕所拥有的像素数,在显示器中,dpi=ppi。dpi强调的是每英寸多少点。同时,屏幕像素密度=分辨率/屏幕尺寸
接着,我们来看一下其他的单位。
设备独立像素:设备独立像素,不同于设备像素(物理像素),它是虚拟化的。比如说css像素,我们常说的10px其实指的就是它。需要注意的是,物理像素开发者是无法获取的,它是自然存在的一种东西,该是多少就是多少。
设备像素比:缩写简称dpr,也就是我们经常在谷歌控制台移动端调试顶端会看到的一个值。设备像素比 = 设备像素 / css像素(垂直方向或水平方向)。可以通过JS来获取: window.devicePixelRatio
2.(布局)视口理解
-
在 PC 端,视口指的是浏览器的可视区域,其宽度和浏览器窗口的宽度保持一致。在 CSS 标准文档中,视口也被称为初始包含块,它是所有 CSS 百分比宽度推算的根源,给 CSS 布局限制了一个最大宽度。
-
而移动端则较为复杂,它涉及到三个视口:
布局视口(Layout Viewport)
、视觉视口(Visual Viewport)
和理想视口(Ideal Viewport)
。
移动端中的三种视口
-
布局视口(layout viewport)
布局视口使视口与移动端浏览器屏幕宽度完全独立开。CSS 布局将会根据它来进行计算,并被它约束。
一般移动设备的浏览器都默认设置了一个 viewport 元标签,定义一个虚拟的布局视口(layout viewport),用于解决早期的页面在手机上显示的问题。iOS, Android 基本都将这个视口分辨率设置为 980px,所以 PC 上的网页基本能在手机上呈现,只不过元素看上去很小,一般默认可以通过手动缩放网页。默认的布局视口宽度为 980px。如果要显式设置布局视口,可以使用 HTML 中的 meta 标签:
<--将布局视口的宽度设定为设备的宽度,此时也就是下面介绍的理想视口-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
布局视口的宽度/高度可以通过 document.documentElement.clientWidth / Height
获取。
视觉视口(visual viewport)
视觉视口是用户当前看到的区域,用户可以通过缩放操作视觉视口,同时不会影响布局视口。
理想视口(ideal viewport)
理想视口的值其实就是屏幕分辨率的值。布局视口的默认宽度并不是一个理想的宽度,于是 Apple 和其他浏览器厂商引入了理想视口的概念,它对设备而言是最理想的布局视口尺寸。显示在理想视口中的网站具有最理想的宽度,用户无需进行缩放。
3.Retina屏幕与模糊的由来
1)dpr的具体表现
有时候我们会发现,当我们在适某一机型的时候,显示上没什么问题。但是一旦我换到另外一部手机,发现出现了模糊的情况,尤其以图片更为显著。
其实这个问题,就是涉及到了上面讲到的一个属性:设备像素比,即我们经常说的dpr。下面先来看dpr的表现:
假设现在有一台iphone6,那么它的设备独立像素是375x667,dpr为2,尺寸是4.7in,那么物理像素就是750x1334。 同样的我们也有一台不知名的设备,它的设备独立像素刚好也是375x667,尺寸也是4.7in,但是dpr为1,此时的物理像素就是375x667。
于是,它们的屏幕表现如下:
在不同的屏幕上,无论是普通屏幕还是retina屏幕,css像素所呈现的大小是一致的。(如果不理解这句话,可以写一个2px的正方形使用谷歌控制台移动设备调试,在不同的设备之间来回切换,你会发现大小其实是一样的。一开始我总以为这个css像素的实际宽高因为受到dpr的影响而在不同设备上的长宽是不一致的。)
不同的是,1个css像素对应(覆盖)的物理像素个数。
所以,如果我们想要在这两个屏幕显示这么一个css样式:
width: 2px;
heigth: 2px;
在普通屏幕下,也就是dpr为1的屏幕中,1个css像素对应(覆盖)的是一个物理像素。在retina屏幕下,1个css像素对应(覆盖)的是4个物理像素。换句话说,就是dpr为2的设备。看下面这张图:
浅显的理解就是可以看作是2cmx2cm的正方形被切割成四块,然后遇到dpr为2的时候,被切割的四块又被分别切割成四块,但是总面积不变。
2)模糊的产生
知道了1个css像素覆盖的物理像素可能不同,就好理解为什么会出现模糊的情况了。
这里又讲到一个名词:位图像素。
位图像素是栅格图像(如:png,jpg,gif等)最小的数据单元。每一个位图像素都包含着一些自身的显示信息。(如:显示位置,颜色值,透明度等)
理论上来说,1个位图像素对应1个物理像素,图片才能达到完美清晰的展示。
但是上面说过,在retina屏幕上,会出现1个位图像素对应多个物理像素。
还是以iphone6为例,1个位图像素对应4个物理像素。由于单个位图像素已经是最小的数据单位了,它不能再被进行切割。于是为了能够显示出来,就只能就近取色,从而导致所谓的图片模糊问题。如下:
3)如何解决
很明显,由于位图像素不够分而产生模糊的情况,解决的办法十分简单,就是使用跟dpr同个倍数大小的图片。比如iphone6,一个200x300的 img
标签,原图就要提供400x600的大小。
那么当加载到 img
标签中,浏览器会自动对每1px的css像素减半,可以理解为此时还是维持着1:1的css像素:物理像素,不产生模糊。
(二) 几种适配移动端的方案
1. 固定布局(pc端)(静态布局)
以像素作为页面的基本单位,不管设备和浏览器宽度,只设计一套尺寸(这种不太现实)
2. 媒体查询
媒体查询可以让我们根据设备显示器的特性(如视口宽度、屏幕比例、设备方向:横向或纵向)为其设定CSS样式,媒体查询由媒体类型和一个或多个检测媒体特性的条件表达式组成。
/* 大屏幕 */
@media only screen and (min-width:1200px) {
对应的样式
}
/* 中等屏幕 */
@media only screen and (min-width: 992px)and (max-width: 1199px) {
对应的样式
}
/* 小屏幕 */
页面大于 768px, 小于 991px 时显示的样式效果
@media only screen and (min-width: 768px)and (max-width: 991px) {
对应的样式
}
/* 手机端显示 */
屏幕小于 767px 时的样式
@media only screen and (max-width: 767px) {
对应的样式
}
3. rem+vw(常用)
1)样例引入
假如我们拿到一个宽度为750px的设计稿,现需要我们编写一套css代码就能适应不同的设备尺寸(以iphone6-375px举例),那我们最希望的是页面中元素的尺寸能够根据设备的尺寸自动进行等比例的缩放,那么要如何实现呢?
2)回顾CSS单位
首先,我们先来回顾一下CSS中的几个单位:px、em、rem、vh、vw、%
-
px
px就是pixel(像素)的缩写,是CSS中相对长度的单位,相对于屏幕显示器的分辨率而言的。
要注意,CSS中的px属于逻辑像素
逻辑像素 = 设备(物理)像素 × 缩放因子(dpr)
例如:iphone6逻辑像素为375 * 667,分辨率为750 * 1334,缩放因子为2。1个逻辑像素 = 设备宽度的1/375;1个设备像素 = 设备宽度的1/750。所以:1/375 = 1/750 * 2
对于桌面设备,逻辑像素通常就等同于物理像素,本来是不用考虑缩放问题的。一切问题的根源就在于:屏幕变得越来越高清,ppi 越来越大。比如我现在用的 PC 是1920px的高清屏,如果没有缩放,所有的东西看起来都会比较小,因此需要放大。
所以决定缩放因子大小的,就是像素密度,密度越大、越高清的屏幕,需要的缩放比例就越大。
-
em
em是相对长度单位。相对于当前对象内文本的字体尺寸(参考物是父元素的font-size)
如当前父元素的字体尺寸未设置,则相对于浏览器的默认字体尺寸
特点:
1. em的值并不是固定的;
2. em会继承父级元素的字体大小
-
rem
rem是CSS3新增的一个相对单位,rem是相对于**HTML根元素的字体大小(font-size)**来计算的长度单位
如果你没有设置html的字体大小,就会以浏览器默认字体大小,一般是16px
-
vw和vh
vm、vh、vmin、vmax是一种视窗单位,也是相对单位。它相对的不是父节点或者页面的根节点。而是由视口(Viewport)大小来决定的(在上面也介绍过)
具体描述如下:
- vw:视口宽度的百分比(1vw 代表视窗的宽度为 1%)
- vh:视口高度的百分比
- vmin:取当前Vw和Vh中较小的那一个值
- **vmax:**取当前Vw和Vh中较大的那一个值
-
%
% 百分比,相对长度单位,相对于父元素的百分比值
3)用rem解决问题
要实现页面中元素的尺寸能够根据设备的尺寸自动进行等比例的缩放,那我们就要先获取设备的尺寸大小,
(可以在理想视口的前提下,利用document.documentElement.clientWidth
获取设备的大小)
再根据设计稿和设备的尺寸确定缩放的比例,对元素进行缩放。
此时就可以用上我们的rem单位,rem是相对于根元素的,我们可以将设备的根元素html的font-size设置为
(设备大小/设计稿大小)× 设计稿的html的font-size大小
(例子中则为(375/750)*16px=8px)
但是这样就会有一个问题:如果不对设计稿的html的font-size做修改,设备根元素的font-size可能会小于浏览器允许的font-size最小值(12px),所以我们要对设计稿的html的font-size大小进行放大,同样地,根据计算公式,设备的根元素html的font-size也会被放大,就能解决这个问题了。
至于要放大多少,为了方便计算最终化为rem单位的值,我们可以将设计稿的html的font-size设定为100px。
这样我们从设计稿的px变到设备的rem只需要把小数点往左移2位即可。
下面举个例子说明:(以图中设计稿和设备的比例为例)
假如在设计稿中有一张 200px * 200px的图片,由于html中font-size=100px,所以在设计稿中,我们可以用2rem*2rem 表示,我们想要在设备显示中元素能够等比例缩放,于是在设备中的图片是100px * 100px的大小,而设备中的html中font-size根据上面的计算公式得到是50px,则图片尺寸用rem也表示为2rem * 2rem,也就是上面说的 ”从设计稿的px变到设备的rem只需要把小数点往左移2位“。
代码实现设置根元素的font-size大小:
(function(doc, win) {
var docEl = doc.documentElement,
// 判断window中是否有orientationchange方法
resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
recalc = function() {
var clientWidth = docEl.clientWidth;
if (!clientWidth) return;
//设置基础html的fontsize
docEl.style.fontSize = 100 * (clientWidth / 320) + 'px';
console.log(docEl.style.fontSize, 'docEl.style.fontSize');
};
if (!doc.addEventListener) return;
win.addEventListener(resizeEvt, recalc, false);
doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);
4)rem+vw
由于vw是相对于当前视口的尺寸大小,我们可以不用js代码获取设备尺寸后再进行换算,确定html的font-size,直接使用vw单位来确定font-size的值。
我们还是以750px的设计稿和375px的设备为例:
此时100vw对应750px,所以1px = 100/750 vw = 0.133333 vw,为了方便计算,也像上面放大100倍,将html的font-size设置为13.33333vw,相当于用下面这一行代码
html{ font-size:13.33333vw }
实现了根据设备尺寸自动修改html的font-size大小,然后还是按照上面的px-rem换算,用rem单位写样式。