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

像素单位

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

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

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

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

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

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

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

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

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

你可以通过操作系统的分辨率设置来改变设备独立像素的大小,但在前端开发的时候我们完全可以将它们当作定值。(没人会闲着无聊频繁改变操作系统分辨率)

我屏幕的设备分辨率是1920*1200(单位:设备像素),当前的分辨率设置下逻辑分辨率是1280*800(单位:设备独立像素)。从图中可以验证,横、纵方向的设备像素数量恰好是设备独立像素的1.5倍。这也意味着,设备独立像素的边长是设备像素边长的1.5倍。

css像素

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

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值,可以看出它不受缩放的影响。

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

例子


我的屏幕宽度是1280个设备独立像素。这个值可以直接通过window.screen.width获得,或者自己根据操作系统的缩放比例和显示器物理像素宽度来计算。我将div宽度也设为了1280px(css像素),当缩放为100%的时候,DIV恰好撑满整个屏幕,不会出现横向滚动条。(这说明缩放比例为100%的时候一个CSS像素完全等于一个设备独立像素。)


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


将窗口最大化,并通过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中,
        不同的属性使用不同的单位:
        @media (max-width: 640px) 中,px单位是css像素;
        而@media (max-device-width: 640px) 中,px单位是设备独立像素。
        参考 https://stackoverflow.com/a/4189911 */

      /* 这里px的单位是css像素,与缩放比例有关!
        也就是说,通过缩放窗口,可以触发以下样式的切换。 */
      @media (max-width: 640px) {
        #box {
          background-color: aqua;
        }
      }
      /* 这里px的单位是设备独立像素,与缩放比例无关!
        也就是说,仅仅通过缩放窗口,不可能触发以下样式的切换。 */
      @media (max-device-width: 640px) {
        #box {
          border-color: blue;
        }
      }
    </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个概念,来解释响应式布局中有关的概念和属性。

03-05 15:14