学习链接
首先,之所以会有1px的适配问题,是因为移动端的一个物理像素并不等于一个CSS像素,这之间存在一个叫做设备像素比的关系,也就是dpr,具体的计算是设备的物理像素和CSS像素的比值。也就是说,当dpr=2时,表示的是设备会用两个物理像素来渲染一个CSS像素,所以此时如果直接在代码中设置1px实际上得到的是两个物理像素的效果,也就是说,会比较宽。
关于这个问题处理方案主要有两种,以border:1px为例子,一种是通过伪元素配合缩放,具体而言就是给当前的元素设置一个content为空字符的after伪元素,接着,将其设置为绝对定位,并且left和top都为0,然后将weight和height都设置为200%,此时整个伪元素的大小是父元素的两倍大,但是border依旧是1px,接着利用tranform的scale缩放,将其缩小一半,同时,设置起始点为左上角,此时的border会跟着宽度和高度一起缩小,所以就得到了真正的1px。
另一种是通过viewport配合rem的方式来实现,将所有的单位都从px转换成rem,但是border的1px不转换,然后在js中将name为viewport的meta标签,设置为缩小固定的比例,这个比例的值具体而言就是用1除以dpr的值,也就是使得此时CSS像素的一个px就等于一个物理像素。
移动端 1px 适配
1px 问题指的是:在一些 Retina屏幕
的机型上,移动端页面的 1px 会变得很粗,呈现出不止 1px 的效果。
原因很简单——CSS 中的 1px 并不能和移动设备上的 1px 划等号。
它们之间的比例关系有一个专门的属性来描述:
DPR (devicePixelRatio) 设备像素比,它是默认缩放为100%的情况下,设备像素和 CSS 像素的比值。
window.devicePixelRatio = 设备的物理像素 / CSS像素
dpr = 2
就意味着设置的 1px CSS 像素,在这个设备上实际会用 2 个物理像素单元来进行渲染,所以实际看到的一定会比 1px 粗一些。
viewport
+ rem
将其他单位都转换为 rem 但是不转换 1px。
这个思路就是对 meta 标签里几个关键属性下手:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
属性 | 含义 | 取值 |
---|---|---|
width | 定义视口的宽度,单位为像素 | 正整数或设备宽度 device-width |
height | 定义视口的高度,单位为像素 | 正整数或设备高度 device-height |
initial-scale | 定义初始缩放值 | 整数或小数 |
minimum-scale | 定义缩小最小比例,它必须 ≤ maximum-scale 的值 | 整数或小数 |
maximum-scale | 定义放大最大比例,它必须 ≥ minimum-scale 的值 | 整数或小数 |
user-scalable | 定义是否允许用户手动缩放页面,默认值 yes | yes/no |
const scale = 1 / window.devicePixelRatio;
const metaEl = document.querySelector("meta[name=viewport]");
metaEl.setAttribute('content', `width=device-width,user-scalable=no,initial-scale=${scale},maximum-scale=${scale},minimum-scale=${scale}`);
const docEl = document.documentElement;
const fontsize = 32 * (docEl.clientWidth / 750) + 'px';
docEl.style.fontSize = fontsize;
伪元素先放大后缩小
这个方法的可行性会较高,兼容性也更好。唯一的缺点是代码会变多。
思路是先放大、后缩小:
在目标元素的后面追加一个 ::after
伪元素,让这个元素定位为 absolute
之后、整个伸展开铺在目标元素上,然后把它的宽和高都设置为目标元素的两倍,border
值设为 1px。
接着借助 CSS 动画特效中的放缩能力,把整个伪元素缩小为原来的 50%。此时,伪元素的宽高刚好可以和原有的目标元素对齐,而 border 也缩小为了 1px 的二分之一,间接地实现了 0.5px 的效果。
代码如下:
#container {
position: relative;
}
#container::after{
content: "";
position: absolute;
top: 0;
left: 0;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: left top;
border: 1px solid #333;
}