为什么会存在 1px 问题?怎么解决?

在项目开发中一直深受
1px
的困扰,移动端展示的样式不是偏粗就是偏细、甚至无法看清。也许大家都尝试过或正在使用着各种解决方案,可是对于物理像素、逻辑像素、设备像素比等概念到底是什么,为什么会产生
1
像素等问题始终是一头雾水。。在进行了一番调研后,发现网上对于一些细节原理描述的都不太清晰。故本文结合了个人的一些理解,重点对其原理及实现进行探讨,希望能对像素相关问题彻底解惑。

为了便于更好的理解本文,下面对像素相关概念进行梳理。

像素

px

是图像显示的基本单元,相对单位。

设备像素(物理像素)

dp

device pixels,显示屏就是由一个个物理像素点组成,屏幕从工厂出来那天起物理像素点就固定不变了。也就是我们经常看到的手机分辨率所描述的数字。

设备独立像素(逻辑像素)

dip

device-independent pixels,就是我们手机的实际视口大小。是操作系统为了方便开发者而提供的一种抽象。程序与操作系统之间描述长度是以设备独立像素为单位。不随页面缩放、浏览器窗口大小而改变。

CSS像素

在 CSS 中使用的 px 都是指 CSS 像素。不考虑缩放情况下,1个 CSS 像素等于1个设备独立像素。

设备像素比

dpr

devicePixelRatio,是物理像素和设备独立像素的比值。

屏幕尺寸

inch

屏幕对角线长度

屏幕分辨率

Resoution

750*1334,手机屏幕纵、横方向像素点数,单位是px。常说的分辨率指的就是物理像素。相同大小的屏幕而言,屏幕分辨率越高显示的像素越多,单个像素尺寸较小,显示效果就越精细。

像素密度

dpi/ppi

概念

描述

dot per inch(pixels per inch),每英寸像素数,通过屏幕尺寸和分辨率来计算像素密度。也是屏幕出厂时就确定了。

简单来说就是像素单位基本分为三种:设备像素(物理像素)、设备独立像素(逻辑像素)、CSS 像素。下文将会围绕相关概念展开讨论。

话不多说,正文开始~~

为什么使用 1px 会出现问题

自从 2010 年 iPhone4 推出了 Retina 屏开始,移动设备屏幕的像素密度越来越高,于是便有了 2 倍屏、3 倍屏的概念。简单来说,就是手机屏幕尺寸没有发生变化,但屏幕的分辨率却提高了一倍,即同样大小的屏幕上,像素多了一倍。

那么我们获取到的 CSS 像素就不是真实的物理像素点了,于是便有了设备像素比的概念( ​​devicePixelRatio​​ 简称 dpr)。它用来描述屏幕物理像素与逻辑像素的比值。不同手机有不同的设备像素比,可参考 ⇲wiki 百科中对视网膜屏的描述 。

CSS 中的 1px 并不等于设备的 1px

对于前端来说,在高清屏出现之前,前端代码的 ​​1px​​​ 即等于手机物理像素点的 ​​1px​​​。但有了 dpr 的概念之后,由于前端代码中的使用的是 CSS 像素,手机会根据 dpr 换算成实际的物理像素大小来渲染页面。比如 iPhone6 的设备像素比 ​​dpr = 2​​​ ,相当于一个 CSS 像素等于两个物理像素,即 ​​1px​​ 由 2个物理像素点组成。

那么问题来了,以 iPhone6 为例,其 ​​dpr = 2​​​、屏幕尺寸(CSS 像素) 为 ​​375x667​​​,一般设计稿提供 2 倍图尺寸为 ​​750x1334​​​ 。那么设计稿中的 ​​1px​​​,对应屏幕尺寸其实应该写成 ​​0.5px​​​。再由 dpr 计算公式可知,​​0.5 * 2 = 1px​​ 物理像素。

此时你应该已经发现了,设计稿要实现 ​​1px​​​ 细线、​​1px​​​ 边框,为什么前端实现总是偏粗的?那是因为如果你在代码中直接写成 ​​1px​​​,再通过 dpr 计算之后其实是 ​​2px​​ 物理像素,并不符合设计稿的要求。

其实设计稿本质上要实现的是 CSS 像素的 !

那么当 ​​dpr=2​​​ 时,代码中直接写成 ​​0.5px​​ 就解决问题了吗?

小数点像素 0.5px 的兼容性问题

其实在项目中,我们已经采用 rem 单位进行了设计稿与屏幕尺寸的换算,即把 ​​1px​​​ 换算成了 ​​0.5px​​ 。但这种方案其实有各种各样的兼容性问题。

PC端

先上结论,在 PC 端浏览器的最小识别像素为 ​​1px​​。

所以在开发阶段,当在开发者工具上进行页面调试时,可以看到即便代码中是 ​​0.5px​​​ ,但默认会被浏览器识别并渲染为 ​​1px​​。所以在浏览器看来总是偏粗了。如果你习惯用 PC 端的页面来进行视觉走查,那么结果可想而知...

为什么会存在 1px 问题?怎么解决?插图亿华云

上图中两个元素 ​​width:200px;height:100px​​​,分别为 ​​border:0.5px​​​、​​border:1px​​​ ,检查元素时可以看到页面上 ​​border=0.5px​​​ 的元素计算大小后和 ​​1px​​​ 效果是一样的,均是 ​​width:202px;height:102px​​​。说明浏览器都识别成 ​​1px​​ 了。

为什么会存在 1px 问题?怎么解决?插图1亿华云

由上面的 gif 图也可以看到,因为设备 ​​dpr=2​​​,所以放大后 ​​1px​​​ 的确是使用了2个物理像素点来渲染。并不是我们想实现的 ​​0.5px​​。

移动端

在手机端,不同手机浏览器对小数点像素的处理效果就更千奇百怪了。

首先我们先来看一下采用 REM 布局方式下,代码中的 0.01rem 到底被换算成了多少?

这里简单说下REM 实现原理

​​rem(font size of the root element)​​​,即根据网页的根元素(​​html​​​)来设置字体大小。和 ​​em(font size of the element)​​​ 的区别是,​​em​​ 是根据其父元素的字体大小来进行设置。

简单来说,rem 布局实现移动端适配的思想是,由于 rem 单位是根据页面根元素的 ​​fontSize​​​ 来计算的,那么将 ​​fontSize​​​ 设置成屏幕宽度 ​​clientWidth​​​ 与设计稿宽度 ​​750​​​ 的比值,那么我们按照设计稿的尺寸来重构页面的时候,使用 rem 单位即自动乘以 ​​fontSize​​ 计算出了适配不同屏幕的尺寸。

// 以750设计稿为例,计算rem font-size

let clientWidth = document.documentElement.clientWidth || document.body.clientWidth;

let ft = (clientWidth / 7.5).toFixed(2);

// 设置页面根字号大小

document.documentElement.style.fontSize = ft "px";

由上面的计算方式可知,不同屏幕宽度会计算出不同 ​​fontSize​​​ ,那么 ​​0.01rem​​ 到底被换算成了多少呢?下面举例计算了几个机型的“1像素”大小

为什么会存在 1px 问题?怎么解决?插图2亿华云

由表格可看出,不同手机计算的“1px”大小差别很大,而且手机本身对小数点的处理情况就存在较大的兼容性问题。

比如 IOS8 系列都已经支持 ​​0.5px​​​ 了,可以借助媒体查询来处理,但是安卓手机对小数像素的表现形式却各不相同。网上关于不同型号手机浏览器对小数点的处理情况的资料较少,只知道在一些低版本的系统里,​​0.5px​​​ 将会被显示为 ​​0px​​​;有的能够画出半个像素的边,有的大于 ​​0.55px​​​ 当成 ​​1px​​​,有的大于 ​​0.75px​​​ 当成 ​​1px​​,从表格计算结果来看是很难直接实现适配的。

比如 HUAWAI P30 的 ​​0.01rem​​​ 计算后为 ​​0.48px​​​ ,这种较小的小数像素其 ​​border​​ 已经无法正常展示了。

为什么会存在 1px 问题?怎么解决?插图3亿华云

那么如何实现 1px 的效果?

在进行一番调研之后,发现目前的实现方案都离不开以下三种。

使用​​伪元素 CSS3``缩放​​的方式使用​​动态

THE END
Copyright © 2024 亿华云