为什么使用1px会出现问题?
css的1px不等于设备的1px
对于前端来说,在高清屏出现之前,前端代码的1px即等于手机物理像素点的1px。但有了dpr的概念之后,由于前端代码中使用的是css像素,手机会根据dpr换算成实际的物理像素大小来渲染页面。比如iphone6的设备像素比dpr=2,相当于1个css像素等于2个物理像素。
那么问题来了,以 iPhone6 为例,其 dpr = 2、屏幕尺寸(CSS 像素) 为 375x667,一般设计稿提供 2 倍图尺寸为 750x1334 。那么设计稿中的 1px,对应屏幕尺寸其实应该写成 0.5px。再由 dpr 计算公式可知,0.5 * 2 = 1px 物理像素。
此时你应该已经发现了,设计稿要实现 1px 细线、1px 边框,为什么前端实现总是偏粗的?那是因为如果你在代码中直接写成 1px,再通过 dpr 计算之后其实是 2px 物理像素,并不符合设计稿的要求。
那么当 dpr=2 时,代码中直接写成 0.5px 就解决问题了吗?
0.5px的兼容性问题
其实在项目中,我们已经采用rem单位进行了设计稿与屏幕尺寸的换算,即把1px换算成了0.5px。但这样会有各种兼容问题。
PC端
在PC端浏览器的最小识别像素为1px。所以在开发中工具书调试的时候,即便代码写的是0.5px,但默认会被浏览器识别并渲染为1px。
所以在浏览器看来总是偏粗了。
移动端
在手机端,不同手机浏览器对小数点像素的处理效果就更千奇百怪了。
从图中可发现,不同手机计算1px大小差别很大,而且手机本身对于小数点的处理情况就存在较大的兼容性问题。
那么如何实现1px的效果
- 使用伪元素 + css3缩放
- 使用viewport + rem布局
- 使用vw单位适配方案
1、伪元素 + css3缩放
常用方法,缩放的方式避免了直接写小像素带来的不同手机的兼容性处理不同。
css3缩放: transform: scale(0.5);
// 通过伪元素实现 0.5px 细线
.line::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 200%;
height: 1px;
background: #b3b4b8;
transform: scale(0.5);
transform-origin: 0 0;
}
为什么要放大200%后再缩小0.5?
为了只缩放border的1px的粗细,而保证border 的大小不变。若直接使用scale(0.5)的话border整体大小也会变为二分之一。
2、动态Viewport + REM方式
该方案参考了阿里早期开源的一个移动端适配解决方案flexible。
initial-scale
缩放值越大,当前 viewport
的宽度就会越小,反之亦然。
比如屏幕宽度是 320px 的话,如果我们设置 initial-scale=2
,此时 viewport
的宽度会变为只有 160px 了。这也好理解,放大了一倍嘛,就是原来 1px 的东西变成 2px 了,但是并不是把原来的 320px 变为 640px ,而是在实际宽度不变的情况下,1px 变得跟原来的 2px 的长度一样了。
Flexible适配方案及问题
Flexible的大致实现思路是,首先根据dpr来动态修改meta标签
中的viewport
中的initial-scale
值,以此来动态改变viewport大小。然后页面统一使用rem来布局。
为什么不直接引用flexible库来进行移动端适配呢?
因为lib- flexible
这个库目前基本废弃了,虽然我们不会去使用flexible库,但其实大多数还是沿用了flexible实现的原理来进行移动端适配。
为什么页面所当比例initial-scale设置为1/dpr?
通过设置缩放比例1/dpr,可将viewport的宽度扩大dpr倍。
当设置 initial-scale = 1 / dpr = 0.5
时,获取到的 viewport
宽度 clientWidth = 750px
,被扩大了 dpr 倍,就正好是设备物理像素的宽度。
CSS像素个数 = 设备独立像素个数 / scale = ( 物理像素个数 / dpr )/ scale
scale = 1 / dpr
// 所以
CSS像素个数 = 物理像素个数
总结
移动端适配主要就分为两方面,一方面要适配不同机型的屏幕尺寸,一方面是对细节像素的处理过程。如果你在项目中直接写了 1px ,由于 dpr 的存在展示导致渲染偏粗,其实是不符合设计稿的要求。