什么是像素
什么是像素
如果你是一位前端开发同学的话,那么你应该听说过像素,有什么像素呢?物理像素,逻辑像素,像素比之类的概念,作为小白的我,一直有听说,但从来没有真正理解。所以这篇文章就来了解下这些。
像素
那么就按照套路来,先聊一下这些官方既定的概念咯
逻辑像素
也就是css像素
,一般程序员开发中使用的尺寸单位,如px,pt
。逻辑像素为软件系统提供了一个更加抽象和统一的图像表示单位,降低了处理的复杂度,提高了兼容性。
这个概念的出现是因为不同屏幕大小不一样,导致同样大小的dom在两端显示不一样的问题。如下图,A屏幕比B屏幕宽一倍,但是中间的元素大小都是一样的,那么B屏幕看起来就很拥挤。
所以就有人思考,能不能将这个dom
元素在不同的屏幕根据一定的缩放比来展示。屏幕小展示的dom就小一点,屏幕大展示的dom就大一点。
像我们在设计dom的时候,假如有一个box
的大小,我们在设计的时候是90px*30px
(逻辑像素),并假定AB两个屏幕的单元物理像素
(每个物理像素点)的大小是相同的且A屏幕的dpr
(设备像素比,后面会说明)是1,。
则A屏幕的box的物理像素也是90*
30*,B屏幕大小是A屏幕长宽的各2/3。那么B屏幕这个box的大小是60*20
(物理像素),也就是A屏幕的2/3。这里注意,我们在控制台查看这个dom的大小时,**不同屏幕的逻辑像素都是一样的90px*30px****,***只是展示的物理像素不一样*。*
物理像素
一个物理像素点就是一个发光的点,是在设备出厂既定的,且是真实存在的。一般一个屏幕的单元物理像素大小是一定的,跟物理设备面板配置有关系。但是整个屏幕的物理像素是可以随着分辨率的大小而变化的。
分辨率是指当前屏幕发光点的个数,如你的分辨率是 3840 x 2160
,那么就是每一行有3840
个发光点,每一列有2160
个发光点。有时候你用截图软件所展示的,其实就是物理像素。
你可能要问了,如果我提高了分辨率,是不是发光点变多,那么在物理区域一定的情况下,是否单元的发光点即物理像素变小了呢?
那么看下上图的是不是你脑中设想的变化呢,如果是的话那我们就是同道中人哈哈哈哈,我原本是以为单元物理像素是发生变化的,但是有些资料告诉我是不变的。屏幕大小固定不变,分辨率增大,发光的点变多,怎么可能每一个发光的点还是原本的大小呢?!
实际上上图这种设想是错误的。单元物理像素点只跟设备有关系,分辨率并不会更改设备像素点的变化。这里我要说下,因为这个问题,我在网上搜索了很多文章,都只是告诉我一些基本的概念,甚至有的文章告诉你“物理像素是会随着分辨率改变的”,看了之后我就更迷茫了。后来用了gpt倒是省略了很多无用文章的筛选,慢慢思索问他问题,最终得出了结论。
回归正题,屏幕大小固定不变,分辨率增大,发光的点变多,但是每一个点的大小还是不变,那只能说明,在分辨率比较低的时候,是有一些物理像素是不参与发光的,可参考下图,其中粉红色笑脸是发光的点,黑白笑脸是不参与发光的点
。
所以现在就能理解,为什么买电脑的时候说分辨率高,图像显示就比较清晰。因为分辨率提高,直接导致了屏幕可以发出的光点数量增加。更多的发光子像素可以组合成更精细和丰富的图像显示效果。
设备像素比
设备像素比(DPR) = 物理像素 / CSS像素
,表示1个CSS像素(逻辑像素)由多少个物理像素组成,设备像素比使网页与不同分辨率的显示器保持一致的渲染效果。一般这个值可以与设备的缩放比是一样的。如下图来看,我的设备的DPR
就是1.5。但是DPR不止于电脑【系统】的缩放比有关,还与浏览器的缩放比有关。【系统里面配置的缩放比】*【浏览器的缩放比】
才是真正的DPR的值
在代码中可以通过JavaScript
中的window.devicePixelRatio
获得当前设备的像素比。
1px问题
不同移动端渲染粗细不一问题
有时候做移动端的项目,你可能会发现1px
的边框,在各个手机怎么不一样,有的粗一些,有的又细一些。
上面我们已经说了,不同设备的dpr
是不同的,随着科技越来越发达,很多手机已经采用了高清屏幕,dpr
一般大于2。你可以看做,越高清的屏幕,物理像素越密集。通过下图可以直观的感受到,当dpr
越高,物理像素越密集,在视觉感官上面就看着要粗一些。
0.5px渲染问题
上面有说各个设备的1px
渲染有视觉差异,那么可不可以根据dpr
来设置css逻辑像素达到大家渲染的物理像素都是一样的,比如dpr
是2,那我就写0.5px
,如果dpr
是3,我就写0.34px
。这样大家都一样咯~
首先看下图的测试,发现有一个规律:
- 当计算的物理像素是小于1,实际上按照1物理像素渲染
- 当计算的物理像素大于1,按照向下取整计算渲染,比如1.5/1.6全部按照1物理像素渲染
而为什么是这样的规则,原因是物理像素是离散的,只能整个整个渲染。
请注意:
- 会出现有的设备小数像素被按照0px处理
- 以上规则只代表我测试的几个设备,不代表所有设备都是这个规则。据了解,也有一些设备的小数物理像素处理,是按照四舍五入处理而不是向下取整处理。
如何解决
伪元素缩放
这种是比较常用的写法,其实就是根据设备的dpr
,对boder
进行不同的缩放比,比如设备的dpr是2,1px对应2个物理像素,那么我写0.5px,就对应1个物理像素了。
也就是把所有的border缩放1/dpr
。但是缩放是针对整个元素的,不仅border
会缩放,整个box
也会被缩放掉,为了避免这种情况,我们在写的时候会默认现将整个box方大dpr
倍,然后在缩1/dpr
倍,整个box
的大小是不变的,只是border
有所改变。(这种缩放会一定程度上改变元素的边框颜色哦,缩放的时候不止尺寸会更改,颜色和字体也会进行缩放)
但是为什么要在伪元素上进行缩放,而不是原有元素呢?
- 不影响元素的实际布局大小。伪元素独立,缩放不会影响原元素大小。
- 避免元素内文字或子元素一起被缩小,造成布局混乱
- 伪元素的内容通常是装饰性的,缩放影响较小。而元素内主要内容不受影响
- 可以方便地针对不同伪元素设置不同的缩放或样式。
// 示例代码,单纯为示例做的样式,具体实现使用需自行改进
@media (-webkit-min-device-pixel-ratio: 3) {
.box34px-after::after {
height: 300px;
width:300px;
transform: scale(1/3);
border: 0.34px solid #000;
content: "";
box-sizing: border-box; // 为了与原元素等大
position: absolute;
transform-origin: 0 0;
}
.box5px-after::after {
height: 300px;
width:300px;
transform: scale(1/3);
border: 0.5px solid #000;
content: "";
box-sizing: border-box; // 为了与原元素等大
position: absolute;
transform-origin: 0 0;
}
.box1px-after::after {
height: 300px;
width:300px;
transform: scale(1/3);
border: 1px solid #000;
content: "";
box-sizing: border-box; // 为了与原元素等大
position: absolute;
transform-origin: 0 0;
}
.box15px-after::after {
height: 300px;
width:300px;
transform: scale(1/3);
border: 1.5px solid #000;
content: "";
box-sizing: border-box; // 为了与原元素等大
position: absolute;
transform-origin: 0 0;
}
}
SVG
这个的原理其实并不是基于物理像素的概念处理的,而是因为svg
中的stroke-width
画线并不是对应css
中的border-width
,而更像是不占空间的outline,具体解释可以参考这个文章:链接 不过确实我们实际项目应该会比较少用这种方案,无论从扩展性还是习惯上。且这两种都有兼容性问题,如果需兼容IE浏览器或其他低版本浏览器的项目谨慎使用。
基于上面两种方案,我做了一个测试,可以看到两种方案的对比:
<div>
<div class="box34px m-b-10 m-r-10 f-s-12 inlineBlock">原0.34border</div>
<div class="box34px-after m-b-10 m-r-10 f-s-12 inlineBlock"></div>
<svg width="100" height="100" class="inlineBlock v-a-m">
<rect width="100" height="100" fill="transparent" stroke-width="0.34" stroke="black" />
</svg>
</div>
<div>
<div class="box5px m-b-10 m-r-10 f-s-12 inlineBlock">原0.5border</div>
<div class="box5px-after m-b-10 m-r-10 f-s-12 inlineBlock"></div>
<svg width="100" height="100" class="inlineBlock v-a-m ">
<rect width="100" height="100" fill="transparent" stroke-width="0.5" stroke="black" />
</svg>
</div>
<div>
<div class="box1px m-b-10 m-r-10 f-s-12 inlineBlock">原1border</div>
<div class="box1px-after m-b-10 m-r-10 f-s-12 inlineBlock"></div>
<svg width="100" height="100" class="inlineBlock v-a-m">
<rect width="100" height="100" fill="transparent" stroke-width="1" stroke="black" />
</svg>
</div>
<div>
<div class="box15px m-b-10 m-r-10 f-s-12 inlineBlock">原1.5border</div>
<div class="box15px-after m-b-10 m-r-10 f-s-12 inlineBlock"></div>
<svg width="100" height="100" class="inlineBlock v-a-m">
<rect width="100" height="100" fill="transparent" stroke-width="1.5" stroke="black" />
</svg>
</div>
其他方案
后续项目遇到更新到这里
------------------------------------------------------------------更新---------------------------------------------------------
rem可以在一定程度上统一1px的粗细
原理:
- rem单位可以基于 html 元素字体大小进行缩放。
- 通过 Javascript 可以检测设备的 dpr 比例,动态设置 html font-size。
- 比如在 dpr=2 的设备上,将 html font-size 设置为设备宽度的一半。
- 这时 1rem 就等于视觉上的 1px。
- 元素使用 rem 单位后,就可以在不同 dpr 设备上缩放到合适的大小。
- 配合媒体查询,可以检测不同 dpr 使用不同的 font-size。
border的1px--->rem表示--->rem由html 元素字体决定---->html 元素字体由设备dpr决定
我目前使用的插件是postcss-px-to-viewport
和postcss-px-to-viewport
可以自定在网上找使用的具体步骤~
因为目前的项目没有很严格要求所有手机的边框粗细做到一定程度的统一,所以上面的方案单纯是我自己简单的测试。如以上阐述或结论有问题,请指正~
转载自:https://juejin.cn/post/7260746629396463675