1px问题由来
在做挪动端我的项目时,有一个逃不掉的问题:在手机上,1px的细线会看起来更宽。
其实这不仅是手机上会呈现的问题,精确来说,这是高清屏的“通病”,在高清的PC上也会同样有。
高清屏是指高dpr的设施,dpr指物理像素/css像素,这样的设施其物理像素的密度更大。又能够细分为两倍屏,三倍屏。
在一般屏,1个css像素只用1个物理像素出现;2倍屏中,1个css像素会用4个物理像素;3倍屏中则是9个。
因为只有依照这样的映射关系,一张图片在不同的设施上,才会显示雷同的大小。
写到这里,仿佛还没有讲清“为什么1px的线在高清屏下会更宽”这个问题。
将高清屏下的像素映射关系代入1px线的场景中,会发现:2倍屏下的线宽是2个物理像素,3倍屏下是3个。
数学中有个概念:线是没有宽度的,点是没有大小的。像素同样是没有大小的。
2倍屏的物理像素密度是一般屏的两倍,并不是每一个物理像素大小是一般屏的1/4,而是物理像素的间距是一般屏间距的1/2。
2倍屏下用两排像素去展现,天然会比一般屏下用一排像素去展现,看起来更粗。
如何修改1px问题
要解决1px问题,实质就是让高清屏用一个物理像素去展现一个css像素。
最简略粗犷的形式:在2倍屏下将1px的细线写成border:0.5px
。但这种办法只在iOS上反对,安卓上会显示成被当成0px
解决。
更通用的计划中,有svg和伪类元素两种。
SVG计划
SVG指的是矢量图,具体在代码中,会作为xml标签组装在html文件中。
我用svg和css两种形式别离实现了两个100px,边框宽为1的矩形;高清屏下成果如下:
<svg xmlns="custom-namespace" width="100" height="100"> <rect width="100" height="100" fill="transparent" // 宽度1px stroke-width="1" stroke="black" /> </svg> <div style=" width: 100px; height: 100px; // 宽度1px border: 1px solid black; box-sizing: border-box; "></div>
stroke-width和border-width一样,将矩形的边宽设为了1px,然而用svg实现的矩形边框看起来却更细。
要害的中央是,应用svg标记的视口大小和应用rect标记的矩形大小是雷同的。
上面用一个比拟形象的图来解释:
(用svg的stroke-width画一个100px大小+1px边宽的方形)
(用css的border-width画一个100px大小+1px边宽的方形)
svg中的stroke-width画线并不是对应css中的border-width,而更像是不占空间的outline。
因为不占空间,它会以图形的边界为核心画线,一条线一半宽度在矩形内,一半在矩形外。而视口大小正好就是矩形的大小,看到的线宽就只有一半了。
为了佐证,能够把画的矩形放大一点,不占满视口,能够看出,这时候和没有解决过的1px一样粗了。
实际操作
以上是对于svg的基础知识,但理论的业务代码必定不会间接这样应用,因为代码的可扩展性太低。
通常会应用postcss-write-svg
这个插件,让咱们间接在css文件定义svg
// 定义svg函数@svg custom-name { width: 4px; height: 4px; @rect { fill: transparent; width: 100%; height: 100%; stroke-width: 1; stroke: var(--color, black); }}.svg-retina-border { border: 1px solid; border-image: svg(custom-name param(--color green)) 1 repeat;}.normal-border { border: px solid green;}
伪类元素计划
这种计划借助伪类元素::before
,在须要增加边框的元素之上加一个“蒙层”。
.target { position: relative;}.target::before { width: 200%; height: 200%; border: 1px solid #333; transform: scale(0.5); content: ''; position: absolute; top: 0px; right: 0px; transform-origin: left top; box-sizing: border-box; pointer-events: none;}
以二倍屏为例,上述是Demo代码,咱们将蒙层的宽高都设置为指标元素的2倍,边框宽度是1px,而后将它进行图形变换transform: scale(0.5)
,整体宽高为0.5倍。
通过两次尺寸的变换,这个蒙层的大小和指标元素保持一致,然而border只有0.5px。
最初的成果如下:
<div class="retina-border">retina border</div><br /><div class="normal-border">normal border</div>
该选哪种计划
两种计划的兼容性和灵活性比照如下:
- 兼容性
svg计划须要思考border-image的兼容性,伪类元素计划须要思考transform的兼容性。svg的兼容性更好。
- 灵活性
因为svg只能画出特定的形态,所以无奈实现圆角边框。而伪类元素计划能够。伪类元素灵活性更好。
综合上述的思考,咱们的项目选择的是伪类元素计划,因为应用圆角边框的中央很多。而且从两种计划的篇幅不难看出来,这个计划的学习老本也低很多。