meta标签到底做了什么事情
做过移动端适配的小伙伴一定有遇到过这行代码:
name="viewport" content="width=device-width, initial-scale=1.0">
但是,很多小伙伴只是感性的认识:噢,我加了这行代码,然后页面的宽度就会跟我的设备宽度一致。然而,这种理解是很片面的。那么,这句话的本质到底是什么呢?
不急,我们先往下面看,这里先留个悬念。
几个专有名词和单位
这里,我们先来辨析一下在适配的时候经常会遇到的一些名词、数值单位。
首先,先来看一下物理像素。
以iphone6为例,可知道:
分辨率
:1334pt x 750pt
指的是屏幕上垂直有1136个物理像素,水平有750个物理像素。
屏幕尺寸
:4.7in
注意英寸是长度单位,不是面积单位。4.7英寸指的是屏幕对角线的长度,1英寸等于2.54cm。
屏幕像素密度
:326ppi
指的是每英寸屏幕所拥有的像素数,在显示器中,dpi=ppi。dpi强调的是每英寸多少点。同时,屏幕像素密度=分辨率/屏幕尺寸
接着,我们来看一下其他的单位。
设备独立像素
:设备独立像素,不同于设备像素(物理像素),它是虚拟化的。比如说css像素,我们常说的10px其实指的就是它。需要注意的是,物理像素开发者是无法获取的,它是自然存在的一种东西,该是多少就是多少。
设备像素比
:缩写简称dpr,也就是我们经常在谷歌控制台移动端调试顶端会看到的一个值。设备像素比 = 设备像素 / css像素(垂直方向或水平方向)。可以通过JS来获取:
window.devicePixelRatio
PC和移动端不同的视口
注:以下涉及的像素均为CSS像素。并且默认不考虑缩放。
布局视口
写过css的小伙伴应该知道,我们在
html
、
body
设置
width:100%;height:100%;
的时候,它并不是无效的。我们都知道
100%
这种百分数应该是继承父元素而来的。那在这里是继承哪里的呢?
在PC浏览器中,有一个用来约束CSS布局视口的东西,又叫做初始包含块。这也就是所有宽高继承的由来。除去
margin
、
padding
,布局视口和浏览器可视窗口宽度是一致的,同时也和浏览器本身的宽度一致。
但是在移动端,就大不一样了。
以下的例子是在不加
meta
标签的前提下进行演示的。
假如我们现在做一个二八分的左右布局,那么如果在PC端上面的话,显示的效果非常完美,这没什么好说的。
那如果是在手机端呢,这里以iphone6为例子来讲解:
图例如下:
代码如下:
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
width: 100%;
}
.left {
float: left;
width: 20%;
height: 100%;
background: red;
}
.right {
float: right;
width:
80%;
height: 100%;
background: green;
}
----
class="left">
class="right">
这里我们会看到,为什么
body
的高度是
980px
,而浏览器的宽度只有
375px
,那么这个
980px
到底是从哪里来的呢?
其实,这里的
980px
就是移动端所谓的布局视口了。
在移动端,默认的情况下,布局视口的宽度是要远远大于浏览器的宽度的。这两个视口不同于PC端,是相互独立存在的。为什么呢?试想一下,如果一个网页不对移动端进行适配,用户进行阅读的时候,如果默认情况下布局视口的宽度等于浏览器宽度,那是不是展示起来更加的不友好。也就是说,如果一个
div
的宽度为20%,那么它在布局视口宽度为
980px
的时候,展示给用户的像素还有196px,而如果宽度只有
375px
的情况下,宽度只有
75px
,展示的大小相差特别大。
所以,浏览器厂商为了让用户在小屏幕下网页也能够显示地很好,所以把布局视口宽度设置地很大,一般在
768px~1024px
之间,最常见的宽度是
980px
。这个宽度可以通过
document.documentElement.clientWidth
得到。
视觉视口
对于视觉视口来说,这个东西是呈现给用户的,它是用户看到网页区域内CSS像素的数量。由于用户可以自行进行缩放控制,所以这个视口并不是开发者需要重点关注的。
值得注意的是,在移动端缩放不会改变布局视口的宽度,当缩小的时候,屏幕覆盖的css像素变多,视觉视口变大,反之亦然。
而在PC端,缩放对应布局宽度和视觉窗口宽度都是联动的。而浏览器宽度本身是固定的,无论怎么缩放都不受影响。
如果对上面的宽度还是很乱,那么这里有一个表格可以帮助你理清思路。
以下表格横向都以浏览器窗口的宽度作为基准:
对于PC端来说:
对于移动端来说:
理想视口
以上,布局视口很明显对用户十分的不友好,完全忽略了手机本来的尺寸。
所以苹果引入了理想视口的概念,它是对设备来说最理想的布局视口尺寸。理想视口中的网页用户最理想的宽度,用户进入页面的时候不需要缩放。
那么很明显,所谓的理想宽度就是浏览器(屏幕)的宽度了。
所以就有了下面的这段代码:
name="viewport" content="width=device-width">
然而,这段代码其实也并不完美,在IE浏览器中,由于横屏竖屏的切换会对其造成影响,为了解决这个兼容性的问题,最后再加上一句,就有了现在的:
name="viewport" content="width=device-width,initial-scale=1">
initial-scale=1
的意思是初始缩放的比例是1,使用它的时候,同时也会将布局视口的尺寸设置为缩放后的尺寸。而缩放的尺寸就是基于屏幕的宽度来的,也就起到了和
width=device-width```同样的效果。
另外,值得一提的是,我们在进行媒体查询的时候,查询的宽度值其实也是布局视口的宽度值。
Retina屏幕&普通屏幕,模糊的由来
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的时候,被切割的四块又被分别切割成四块,但是总面积不变。
模糊的产生
知道了1个css像素覆盖的物理像素可能不同,就好理解为什么会出现模糊的情况了。
这里又讲到一个名词:位图像素。
位图像素是栅格图像(如:png,jpg,gif等)最小的数据单元。每一个位图像素都包含着一些自身的显示信息。(如:显示位置,颜色值,透明度等)
理论上来说,
1个位图像素对应1个物理像素,图片才能等到完美清晰的展示
。
但是上面说过,在retina屏幕上,会出现1个位图像素对应多个物理像素。
还是以iphone6为例,1个位图像素对应4个物理像素。由于单个位图像素已经是最小的数据单位了,它不能再被进行切割。于是为了能够显示出来,就只能就近取色,从而导致所谓的图片模糊问题。如下:
如何解决
很明显,由于位图像素不够分而产生模糊的情况,解决的办法十分简单,就是使用跟dpr同个倍数大小的图片。比如iphone6,一个200x300的
img
标签,原图就要提供400x600的大小。
那么当加载到
img
标签中,浏览器会自动对每1px的css像素减半,可以理解为此时还是维持着1:1的css像素:物理像素,不产生模糊。
这个做法其实就是手淘团队在做retina适配的一个重要的原理之一,后面会讲到,这里先放着不说。
其他
反向思考一下,如果普通屏幕,也就是dpr为1的屏幕,也使用了两倍的图片,会发生什么样的情况呢?
很明显,在普通屏幕下,200×300的
img
标签,所对应的物理像素个数就是200×300个,而两倍图片的位图像素个数则是200x300x4,于是就出现一个物理像素点对应4个位图像素点,所以它的取色也只能通过一定的算法进行缩减,显示结果就是一张只有原图像素总数四分之一,肉眼看上去虽然图片不会模糊,但是会觉得有点色差。(其实就是模糊的逆向过程)
用图片来表示就是:
这里摘取了网上一篇博文的demo来阐述上面所说的问题。
以上是一张100x100的图片,分别放在了100x100,50x50,25x25的容器中,在retina屏幕下面的显示效果。
通过取色器放大镜可以看出边界像素点的差别:
在图一中,边界像素点就近取色,色值介于红白之间,偏淡,图片看上去会模糊(可以理解为图片拉伸)。
在图二中,图片正常,很清晰。
在图三中,边界像素点就近取色,色值介于红白之间,偏浓,图片看上去有色差。
手淘团队flexible.js布局
现今,适配手机端的传统rem布局已经逐步被手淘团队的一套flexible布局代替。
具体的实现方式以及细节这里也不铺开来说,具体参考w3cplus的一篇文章,很容易读懂和理解。
这里我更想分析一下flexible.js做法的意义和原因。