一些关于 Viewport 与 device-width 的东西

# CSS pixel 与 device pixels (css px 与 device px)

首先了解下什么是 CSS pixels,什么是 device pixels

  1. CSS pixels 可以理解为 css 像素,是浏览器使用的抽象单位,主要用来在网页上绘制内容。也可以说 css px 是专门为 web 开发者提出的一个抽象概念,也只跟我们平时写的 css 样式有关,与分辨率(比如 720px*1280px)里的 px 没关系。
  2. device pixels 可以理解为是设备像素,官方的解释是:显示屏幕的最小物理单位,每个 dp 包含自己的颜色、高宽等。

对于我们做 web 开发的人来说,我们用到的最多的地方是 css 像素,即类似 width:300px;font-size:14px; 这类的 css 语句里用到的。它跟设备自身的 px 是没关系的。 也就是说 1 个 CSS 像素占多少个物理像素是不确定的,这个问题通过页面的放缩比较容易理解。比如一个普通的 html 页面上,有一个 300px 的元素;如果我们放大页面,元素会占据更多的设备像素(即 device pixels),但它的 CSS pixels 不变,依然是 300px;缩小页面也是同样的道理,占据了更少的 device pixels,但它的 CSS pixels 不变。也就是说,元素占据了多少 device pixels,是由当前页面的放缩比例而定的。

不过,页面的缩放对我们 web 开发来说是好像没有任何意义的。但有一个问题,我们开发出来的页面是想要在各个屏幕个以最佳的样式展示出来的,我们不能让页面里存在不能控制的元素。所以就出现了它:<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0" /> 也就是我们在进行 web 开发的时候经常用到的 viewport。那么 viewport 到底是个什么玩意儿呢,而且 content 里面 width 为什么要等于 device-width 呢,device-width 又是个什么概念呢?下面我们来看看它们哥俩儿是什么。

# Viewport 与 device-width

在解释这两个概念之前,先说一个大家都知道,但不明白为什么的情况:把一个普通的在 PC 上开发的 HTML 页面直接放手机上,你会发现不管多大的页面都可以在小小的手机屏幕上显示,但是图文都会显示的特别小;如果你用 JAVASCRIPT 获取下页面宽度,你会发现,大多数的页面宽度都是 980px 的,这个就跟 viewport 有关。

Viewport 起源于苹果,但现在已经被大多数浏览器支持。关于 viewport 有两个概念 visual viewportlayout viewport。这里 visual viewport 也就是视觉上的窗口,可以理解为设备自己的宽度。这时,如果你有一个长 960 的页面,有一个元素是 20%(实际解析出来就是 192px)。但如果我用宽为 320px 的屏幕打开呢,这个元素就成了 64px,但是这个时候我设置的字体大小是 12px,在 320px 的屏幕上就只能显示 64/12 个字了~

鉴于这个问题,苹果找到一种解决方法,在移动版的 safari 中定义了 viewport meta 标签,它的作用就是创建一个虚拟的窗口(viewport),这个虚拟的窗口就是 layout viewport 分辨率接近桌面显示器,apple 将其定位为 980px。在手机上,可以通过 document.documentElement.clientWidth 来获取。

其他浏览器厂商也有不同的解决办法,例如 UCweb 是使用的中间技术。

另外据说,不同浏览器厂商对于 layout viewport 的大小都有自己的定义。但是,我用手上不多的几个手机(小米 2s、SONY、金立、ipad)测试的结果,都是 980px。其中在小米 2s 上测试了小米自带浏览器、QQ 浏览器、UC 浏览器、谷歌浏览器,还有 opera 浏览器,结果都一样;其他的手机上面不管装了什么浏览器,都测试了,结果都是 980px。但是这个值对于我们 web 开发来说,好像也没有什么用处。

因为大多数情况下,<meta name="viewport" content="width=device-width" /> 这个标签对我们来说是最给力的,可以让我们的页面里的图文显示的是正常的,很大程度上提高了页面的可读性。而这一 meta 标签的功能就是设置 layout viewportdevice-width 的宽度。但是 device-width 具体是什么呢?

第一代 iphone 的时候,分辨率为 320*480,屏幕尺寸为 3.5 寸(注意,这个 3.5 寸说的是屏幕的对角线宽),这时候 device-width 就是 320px,也是手机的分辨率宽,此时 device-width 就是设备宽。但第二代的 iphone 分辨率提高为了 480*960,屏幕尺寸为依然为 3.5 寸,如果 device-width 还是设备宽,那么同样是 320px 的页面放 480*960 的手机屏上,图文就会变得比较小,又会影响其可读性。因此 iphone 的 device-width 一直维持在 320px,ipad 一直维持在 1024px。这个时候,device-width 就不是设备宽了(也就不是分辨率的宽了),是一个中间层。Android 采用的也是这一概念,其 device-width 值以 360 居多,但也不乏有像 540px 和 600px 这样的奇葩。在设置了 <meta /> 标签以后,device-width 值可以用 window.innerWidth 来获取 device-width 值。

# 有关 DPI

pi(Dots Per Inch),有时也叫做 ppi(Point Per Inch),翻译过来也叫像素密度,表示设备每英寸所占有的像素数;数值越高,即代表显示屏能够以越高的密度显示图像。(注:这里的像素,指的是 device pixels。)DPI 的计算公式:

有意思的是,这个值比苹果官网上公布的 326 还有高一点点。

其中,PPI 在 120-160 之间的手机被归为低密度手机,160-240 被归为中密度,240-320 被归为高密度,320 以上被归为超高密度(Apple 给了它一个上流的名字——retina)。

这些密度对应着一个特定的缩放比例值,拿我们最熟悉的 iphone4 或 4s 来说,它们的 PPI 是 326,属于超高密度的手机。当我们书写一个宽度为 320px 的页面放到 iphone 中显示,你会发现,它竟然是满宽的。这是因为,页面被默认放大了两倍,也就是 640px,而 iphone4 或 4s 的宽,正是 640px。

# DevicePixelRadio

Android 和 iPhone 中,都有一个占整个屏幕大小的换算单位,Android 中叫 dip 或 dp,iPhone 中叫 point。Dip,就是 device independent pixels,设备独立像素。而设备的物理像素和 dip 的比例就是 devicePixelRatio,此值可以通过 window.devicePixelRatio 属性获取。

据我查找到的一些资料显示,这里的独立像素的值其实就是 device-width 值。这个值也就是我们平时在手机上布局用的宽度,当然是在设置了 <meta /> 标签的前提下。

这里为什么提到 devicePixelRatio 这个概念呢?

因为当 devicePixelRatio=1.5 的时候,也就是说当屏幕分辨率宽与 device-width 的比值为 1.5 时,某些手机自带的浏览器(特别是三星手机出现在最多)会出现 1px 的线解析成 2px 的 bug,但是经测试同一部手机上安装的 UC 浏览器一般不会有这个 bug。关于这个 bug,网上有一篇文章做了比较好的解释:

https://www.imququ.com/post/devicepixelratio-and-border-width.html (opens new window)

文章很多知识引用了《移动 webapp 开发必备知识》和《什么是 viewport,为啥需要 viewport》,链接分别是:

http://www.qianduan.net/mobile-webapp-develop-essential-knowledge.html (opens new window)

http://www.myexception.cn/mobile/428756.html (opens new window)

还有这篇:http://www.quirksmode.org/blog/archives/2010/04/a_pixel_is_not.html (opens new window)