深究移动端适配那些坑(小数像素、图片模糊)

一、适配方案的利弊
缩放
最早是直接用 px 来写,然后用 meta 标签里的 scale 来缩放整个页面,简单粗暴。好处是简单快速,坏处是不能控制部分样式的缩放,一些边框之类的在小屏会变得很细。据说早期天猫首页就是这么干的。其实之后所有适配方案都是这个原理,在编码的时候以设计稿为标准,到手机显示时则根据不同机型的显示宽度而缩放,只是缩放的技术不同。
rem

  • 1rem 等于的 fontSize 大小;
  • rem 适配方案只需要给的 fontSize 设置任意大小。因为设计稿一般为 750px 宽,所以 fontSize 设置为 75px 是比较常见的经验标准,太大或太小对最终结果的精准度可能有影响。之后就可以确定 1rem = 75px,然后在还原设计稿时将 px 计算转成 rem 即可。并且要在 JavaScript 中加入脚本(flexable.js),根据屏幕显示宽度来动态改变的 fontSize,屏幕宽度越大则 fontSize 越大;
  • rem 的优势是根节点 fontSize 可控(即 rem 的缩放标准完全可控),兼容性好。但始终是为了模拟出 vw 的效果,存在多一层转换,麻烦、不直观且精准度稍差;
vw
  • 1vw 等于当前屏幕显示区域的百分之一;
  • vw 就是为动态布局而生的,所以不需要辅助任何脚本。一般设计稿宽度为 750px,那么还原设计稿的时候,1vw = 7.5px,根据这个将设计稿上的 px 换算成 vw 即可;
  • vw优势是精确、便捷,但兼容性稍差,标准的实现比 rem 晚 3 年;
【深究移动端适配那些坑(小数像素、图片模糊)】em
  • em现在一般不用来做移动端适配,这里只是顺带一讲;
  • em 是唯一一个可以在局部实现相对长度的单位;
em 在 font-size 中使用是相对于父元素的字体大小,在其他属性中使用是相对于自身的字体大小,如 width(来自:MDN)
  • em的出现是为了解决文字无法跟着页面一起放大这个问题。在ie6的那个年代,若使用px来设置 font-size ,当用户使用 Ctrl+滚轮 来放大页面时,文字是不会跟着放大的。但是现代的浏览器已经没有这个问题了;
  • 利用 em 根据自身字体动态适配的特性,可以实现很多需求,如根据字体大小动态改变行高、宽度、边距等;
二、逻辑像素和物理像素
逻辑像素就是我们平时所讲的像素,也就是 CSS 中的 px ,所以逻辑像素是一种抽象化的数字单位。而物理像素是指电子屏幕的最小发光单元。
其实最早的时候,不需要这样严格区分,因为那时候 1 逻辑像素就是用 1 物理像素来显示,也就是设配像素比(DPR,物理像素和逻辑像素的比例)为 1。后来 iphone4 打破了这个规则,它那块视网膜屏幕可以用 2 倍物理像素来显示 1 逻辑像素,从而使手机的显示效果更细腻。如下图,即 DPR2 ? 横纵各有 2 个像素。
深究移动端适配那些坑(小数像素、图片模糊)
文章图片

手机还有屏幕像素密度(PPI as Pixels Per Inch)的概念,也就是每英寸所包含的物理像素数量,一般来说在手机的使用视距下 PPI 达到 300 人眼就无法看出屏幕的像素点,这也就是苹果的视网膜屏幕的名称来由。
三、移动端图片模糊
移动端适配图片,由于移动端 dpr 不同,导致相同的位图会在高 dpr 的手机上模糊,需根据 dpr 使用N倍图可解决,即 dpr = 2 使用 2 倍图。
原因是,位图的像素信息(位置、颜色)是固定的,但在高 dpr 的屏幕显示时,会用数倍的物理像素去显示同样的逻辑像素,底层的算法并不会将物理像素一一对应上图片像素,而是进行就近取色,最终导致图片模糊。如下图:
深究移动端适配那些坑(小数像素、图片模糊)
文章图片

为什么要就近取色,而不是直接通过多个物理像素显示同一颜色呢?是因为用数倍物理像素显示一张图片时,如果只是简单粗暴的用 N 个物理像素直接显示 1 个图片像素,在移动端的高素质屏幕下锯齿感会很明显,为了解决这个问题则采用了平滑处理技术(就近取色属于其中一个操作)。假设有一张显示 0 的图片,则下面是处理过程:
深究移动端适配那些坑(小数像素、图片模糊)
文章图片

从上图可以看出,dpr2 在图像的细腻程度上能比 dpr1 处理得更好,所以才不是简单地用 4 个物理像素代替 1 个物理像素。最终结论是不是不能直接取色,而是就近取色更好。
四、小数像素导致的图片比例失调
做移动端适配的时候因为 px 是由 rem/vw 转换后的值,所以肯定会存在小数的情况,小数逻辑像素无法与物理像素完美对齐的,不可能让 1 个完整的物理像素去显示 0.333 的逻辑像素,所以面对这种情况webkit内核会有两种对齐方案:
深究移动端适配那些坑(小数像素、图片模糊)
文章图片

蓝色代表计算尺寸,黑线框代码实际对齐后的尺寸。图片来自:[](https://trac.webkit.org/wiki/...https://trac.webkit.org/wiki/...
enclosingIntRect 是简单地将渲染面积扩大 1px,保证能完全覆盖渲染的物理像素,这个方案只在少部分地方用到,如渲染svg,为了保证盒子能完整包裹矢量图。
pixelSnappedIntRect 则顾名思义,是指在容器内折断像素的方案。但并不是简单的四舍五入,因为如果仅仅四舍五入,会导致最终撑开或与容器相差太大,这个方案是为了保证最终渲染结果跟计算结果至多相差 1px。一个容器内有三张宽 14.25px 的图片真正的大致渲染计算过程是:
  • 首先将容器第一张图宽度四舍五入,那么第一个张图变成 14px 宽;
  • 因为第一张图四舍五入后少了 0.25px ,那第二张图则需要相应加上 0.25px,14.25 + 0.25 = 14.5px,四舍五入后则为 15px;
  • 第二张图多用了 15 - 14.5 = 0.5px,所以第三张图要减去这 0.5px,14.5 - 0.5 四舍五入后为 14px;
  • 最终,容器都计算宽度为 14.25 * 3 = 42.75,实际对齐物理像素后是14 +15 +14 = 43,这跟我们声明的宽度仅误差了 0.25px,所以最终渲染结果与小数像素的误差不超过 1px。
    深究移动端适配那些坑(小数像素、图片模糊)
    文章图片

    深究移动端适配那些坑(小数像素、图片模糊)
    文章图片

从上面的计算结果来看三个 div 中第二个的宽度是不同的,所以这也就是小数像素导致样式偏差的原因。因为这种样式偏差在各种布局盒子里都会存在,但因为在图片上效果更明显(图片挤压),所以通常只在图片显示时需要处理这个问题。目前没有比较好的解决方案,只能尽量写死像素,图片则可以使用 svg。
这一块是关于 LayoutUint 的知识,是用来解决页面缩放时的渲染问题的,具体可以查看 webkit 团队的文章
五、移动端适配方案的思考(来源:[](https://www.zhihu.com/questio...https://www.zhihu.com/questio...):
  • 在嵌入进客户端时,会出现不同尺寸手机的web适配和native适配不一致,导致用户体验割裂;
  • 在设计层面,当客户使用更大屏幕期望地是能看到更多的内容,而这个适配方案却仅仅是让5寸屏幕和4寸屏幕到的信息量是一样的;
  • 关于字体的问题,从用户体验来说用户希望看到的字体是绝对大小的,就像看报纸,多大张的报纸,字体都应该是一样大,有一个最适合阅读的字体大小,而自适应打破了这种设计哲学。知乎的移动端页面,则完全使用了 px;
参考资料:
CSS的值与单位
LayoutUnit & Subpixel Layout
rem 产生的小数像素问题
LayoutUnit
深入了解canvas在移动端绘制模糊的问题

    推荐阅读