【响应式布局】理解设备像素、设备独立像素和css像素

这篇文章是我在个人旧博客上发过的文章,最近又碰到这个问题,整理修改了一下发到这里。css

像素单位

像素单位有设备像素、逻辑像素和CSS像素3种。html

设备像素(device pixels)、设备分辨率

设备像素也叫物理像素。

设备像素指的是显示器上的真实像素,每一个像素的大小是屏幕固有的属性,屏幕出厂之后就不会改变了。
设备分辨率描述的就是这个显示器的宽和高分别是多少个设备像素。
设备像素和设备分辨率交给操做系统来管理,浏览器不知道、也不须要知道设备分辨率的大小,浏览器只须要知道逻辑分辨率就能够了。前端

设备独立像素(Device Independent Pixels)、逻辑分辨率

设备独立像素也叫逻辑像素。

设备独立像素(dips)是操做系统为了方便开发者而提供的一种抽象。应用程序与操做系统之间描述长度时以设备独立像素为单位,而后操做系统再将单位从设备独立像素转化为设备像素,从而控制屏幕上真正的物理像素点。web

为何须要在应用程序与设备像素之间定义这么一种单位呢?为何应用程序不该该直接使用设备像素来描述长度?
随着显示器制造技术愈来愈先进,屏幕像素密度愈来愈高。一样是1920*1080颗像素,之前要放在宽大的显示器中,如今均可以放在手机屏幕上了。本来高度为12个设备像素的字体,如今高度为24个设备像素才能获得相近的大小(这也说明字变得更加清晰锐利了),若是应用程序直接使用设备像素,那么编写应用程序将变得很是困难:字体在一些屏幕上高度为12个设备像素,在另外一些屏幕上却要变为24个设备像素。
所以操做系统定义了一个单位:设备独立像素。操做系统保证:用设备独立像素定义的尺寸,无论屏幕的参数如何,都能以合适的大小显示(这也是设备独立像素名字的由来)。操做系统是如何作到的呢?对于那些像素密度高的屏幕,将多个设备像素划分为一个逻辑像素。至于将多少设备像素划分为一个逻辑像素,这由操做系统决定
对于上面的例子:“本来高度为12个设备像素的字体,如今高度为24个设备像素才能获得相同的大小”,操做系统会将一个逻辑像素定义为2*2个真实像素,从而设备独立像素尺寸不须要改变,并且无论在新、旧设备上,显示的尺寸大体相同。
用4个设备像素来显示一个设备独立像素chrome

设备独立像素与设备像素之间的比例是多少,显示器厂商和操做系统厂商会经过调查研究来得出最利于观看的比例。广泛规律是,屏幕的像素密度越高,就须要更多的设备像素来显示一个设备独立像素。

经过screen.width/height获得的数值就是整个屏幕(不只仅是浏览器的区域)的宽度和高度(单位:设备独立像素)。这个数值不随页面缩放、浏览器窗口大小而改变。segmentfault

逻辑分辨率用屏幕的宽*高来表示(单位:设备独立像素)。浏览器

经过操做系统设置来改变设备独立像素的大小

你能够经过操做系统的分辨率设置来改变设备独立像素的大小,但在前端开发的时候咱们彻底能够将它们看成定值。(没人会闲着无聊频繁改变操做系统分辨率)
经过操做系统设置来手动调节逻辑像素
我屏幕的设备分辨率是1920*1200(单位:设备像素),当前的分辨率设置下逻辑分辨率是1280*800(单位:设备独立像素)。从图中能够验证,横、纵方向的设备像素数量刚好是设备独立像素的1.5倍。这也意味着,设备独立像素的边长是设备像素边长的1.5倍。dom

window.devicePixelRatio在下文会解释。

css像素

在CSS中使用的px都是指css像素,好比width: 128px。css像素的大小是很容易变化的。当咱们缩放页面的时候,元素的css像素数量不会改变,改变的只是每一个css像素的大小。也就是说width: 128px的元素在缩放200%之后,宽度依然是128个css像素,只不过每一个css像素的宽度和高度变为原来的两倍。若是本来元素宽度为128个设备独立像素,那么缩放200%之后元素宽度为256个设备独立像素(css像素宽度始终是128)。chrome-devtools

开发者在开发的时候基本上只用考虑css像素,在这里介绍设备像素和设备独立像素只是为了讲述页面缩放的原理,以及方便之后理解viewport。

css像素与设备独立像素的关系

缩放比例就是css像素边长/设备独立像素边长
根据缩放比例和设备独立像素边长,就能计算出css像素边长。
缩放比例为100%的状况下,一个css像素大小等于一个设备独立像素。布局

window.devicePixelRatio

window.devicePixelRatio设备像素比。
devicePixelRatio = CSS像素边长/设备像素边长。好比devicePixelRatio=2,表示CSS像素的边长是设备像素的2倍,所以在相同长度的直线上,设备像素的数量是CSS像素数量的2倍,须要4个设备像素来显示1个CSS像素。

在桌面浏览器上,缩放会致使CSS像素边长的改变,从而致使window.devicePixelRatio的改变!

可是!当移动端浏览器加入讨论时,事情就变得复杂了一些。事实上,桌面浏览器与移动端浏览器的缩放机制是不同的!桌面浏览器的是page zoom,移动端浏览器的是pinch zoom。
根据CSS标准,计算window.devicePixelRatio时,不考虑pinch zoom对CSS像素尺寸的影响。所以,在移动端缩放不会形成window.devicePixelRatio的改变。经过chrome的远程调试就能测试出这一点。



以上2张图展现了我在移动端浏览器上缩放先后,分别获取到的devicePixelRatio值,能够看出它不受缩放的影响。

page zoom和pinch zoom的主要区别是,前者会形成layout viewport的尺寸(以CSS像素为单位)改变然后者不会。
为何要弄两套缩放机制?这是由于, 在桌面端放大时,用户 不但愿出现横向滚动条,所以须要将 layout viewport(网页布局宽度)始终保持与visual viewport(浏览器窗口可视宽度)相等。在缩放时,它们的 物理尺寸是不变的(好比,以厘米为单位或者以设备像素为单位),可是CSS像素的尺寸会随着缩放变大,结果就是 网页的layout/visual viewport(以CSS像素为单位)变小了(一行只能显示更少的字)。而 在移动端,手机的屏幕很是窄,若是使用一样的机制,用户一放大,网页的layout viewport变小(好比小到只有200CSS像素),布局就会很是恶心。所以,在移动端放大时,浏览器再也不干涉layout viewport,让它随着CSS像素一块儿“膨胀”, layout viewport的宽度(以CSS像素为单位)不变,可是visual viewport的宽度(以CSS像素为单位)变小,所以会出现横向滚动条。(你会发现,在手机上放大网页,一行可以显示的字的数量不变,只不过须要横向滚动才能看完一整行)
ppk大神在 他的文章中也解释了page zoom和pinch zoom。

另外请注意,改变操做系统分辨率会致使设备独立像素边长改变,从而致使CSS像素边长改变(前面已经说过,CSS像素边长是根据缩放比例和设备独立像素计算出来的),从而致使window.devicePixelRatio的改变!网上不少文章不区分设备独立像素和设备像素,那样就没法解释这个现象。

例子


个人屏幕宽度是1280个设备独立像素。这个值能够直接经过window.screen.width得到,或者本身根据操做系统的缩放比例和显示器物理像素宽度来计算。我将div宽度也设为了1280px(css像素),当缩放为100%的时候,DIV刚好撑满整个屏幕,不会出现横向滚动条。(这说明缩放比例为100%的时候一个CSS像素彻底等于一个设备独立像素。)

这里有一个小坑点。 若是纵向滚动条存在的话,它会占据一点点宽度,这时若是我还将元素宽度设为屏幕宽度1280px,屏幕就没法装下整个元素,而后就会出现横向滚动条。
在上面这个例子中由于纵向滚动条不存在,因此没有这个问题,未来在开发的时候要注意。

注意到我设置了5px的橙色边框,为何最终的宽度不是1280+5+5=1290呢?由于我给全部元素加上了box-sizing: border-box的样式,这样我设置的边框宽度就会包含在width中,也就是最终加上边框之后宽度为1280px


当我缩小浏览器窗口的时候滚动条出现了。由于div的宽度没有改变,不管以什么单位衡量(设备像素、设备独立像素仍是CSS像素)。

由于缩放依然是100%,css像素边长/设备独立像素边长依然是1:1。


将窗口最大化,并经过Ctrl+鼠标滚轮将浏览器缩放调整为200%,屏幕只能显示DIV的左半部分了,这时DIV的宽度依然是1280个css像素,可是它宽度变成了2560个设备独立像素。


如下是测试用的简单代码,你们能够本身在Chrome中试试!

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
  <title>test</title>
  <style>
    * {
      padding: 0;
      margin: 0;
      box-sizing: border-box;
    }

    body {
      text-align: center;
    }

    #box {
      /* 这里的width设置为screen.width的值,screen.width以设备独立像素为单位,给出屏幕的宽度。
      从而,在缩放为100%时,#box的宽度刚好等于屏幕宽度。 */
      width: 1280px;
      /* 限制高度防止出现纵向滚动条 */
      max-height: 100vh;
      background: lightblue;
      border: 5px solid orangered;
      border-radius: 20px;
    }

    /*
      media query rule中px的单位是设备独立像素,与缩放比例无关!
      也就是说,仅仅经过缩放窗口,不可能触发如下样式的切换。
    */
    @media (max-width: 640px) {
      #box {
        background-color: aqua;
      }
    }
  </style>
</head>

<body>
  <div id="box">this is box<br> 1
    <br> 2
    <br> 3
    <br> 4
    <br> 5
    <br> 6
    <br> 7
    <br> 8
    <br> 9
    <br> 10
    <br>
  </div>
</body>

</html>

参考资料

  1. A tale of two viewports — part one
  2. A pixel is not a pixel - MDN

欢迎阅读下一篇文章:initial containing block、viewport以及相关尺寸,它使用这篇文章介绍的3个概念,来解释响应式布局中有关的概念和属性。

相关文章
相关标签/搜索