由1px问题引发的rem方案

面试官问,写过 h5 吧?1px 问题遇到过吧?怎么解决呢?

  • 灵魂三问!别急,慢慢分析。

什么是 1px 问题?

  • 一句话总结,就是涉及到 1px 的属性,在某些手机浏览器里,不能正常显示,有时太细甚至不显示;有时太粗,明显宽于 1px。即 1px 这个度量单位失真了。
  • 再延伸一下,这其实是一类失真问题的统称,不止局限于 1px,我们讨论的意义在于,知其原因,晓其方法。

基本概念 ppi dpr

  • 深入讨论之前,先看几个概念。ppi、dpr、dip
  • ppi:pixels per inch。每英寸对角线上所拥有的像素。计算公式为:ppi 手机屏幕的 ppi 当达到一定数值时,人眼就分辨不出颗粒感。
  • dpr:device pixel ratio。设备像素比。有些高清屏幕为了追求高 ppi,增大了设备像素。在原本 1 个格子里加倍了像素,让人眼感觉更加清晰。比如原来是 1x1 的方格,dpr 为 2 时就是 2x2。
  • dip:device independent pixel。设备独立像素,是指一种换算机制。由于高清屏幕像素点过多,如果按照原来的像素数显示,由于屏幕实际尺寸不会变的很大(至少不会 2、3 倍的增加),所以会导致图像过小,以至于人眼反而看不清。所以设备自动进行了换算,得出了这个意为与设备无关的像素值,也可用来描述图像相对的大小。

何时发生?

  • 我们写的 css 像素,实际上就是换算后的 dip。设备根据不同 dpr,再反推出到底应该渲染多少实际的设备像素。
  • 当高清屏 dpr 大于 1 时。相当于放大了整个页面,所有单位相当于放大了 dpr 倍。此时写个 1px,如果 dpr 是 2,那么等于设备应该渲染 0.5px 的 dip。
  • 然而,问题关键来了。有时这个 0.5 会被四舍五入为 1。这样就整整差了 2 倍。所以看起来线条变粗了。

如何解决

方案一大类:跳过会出问题的属性,如 border。用其他方案替代

  • 把 border 颜色设为透明,然后加上 border-image。缺点:有点 low,生生把灵活的 css 变成了硬编码,随便改个啥就得换图。
  • box-shadow 模拟 border,四个方向分别写阴影。缺点:不能圆角了,即 border-radius 无法模拟。颜色也不准确,因为有阴影。
  • 使用伪元素,造个“替身”。先放大 n 倍,再 transform 缩小 1/n。缺点:有些 html 元素不能用伪元素。
  • 用 svg 整个绘图,画出个 1px。然后作为背景图使用,是上面第一个方法的升级。有个插件 postcss-write-svg,可以将 svg 画好插入 css。

方案二大类:模拟 0.5px 或 0.33px,使其生效

  • WWDC2014 上提到了,直接就用 0.5px,ios8 以上支持小数 px,缺点:安卓不鸟你
  • 使用 rem。根据 dpr 动态换算 viewport 的缩放比。如为 2 就缩小到 0.5 倍,为 3 就 0.333。由于整个页面已被缩小,故 1px 会正常显示为缩小后的长度。设计师很满意!程序员喘口气!实测,修改后实际是改变了屏幕视口的宽高,即修改了 dip。也有缺点:缩放比太小时,导致缩小后的 1px,小于了 0.5px,有时还是会被四舍五入舍去。可以规避,缩放比别太小即可。