移动端适配:为什么伪类元素可以修正retina屏下1px问题?

242 阅读3分钟

伪类元素方案

效果如下

// index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" type="text/css" href="./index.css" />
  </head>
  <body>
    <div class="retina-border">retina border</div>
    <br />
    <div class="normal-border">normal border</div>
  </body>
</html>
// index.css
.retina-border {
  position: relative;
}

.retina-border::before {
  content'';
  position: absolute;
  width100%;
  height100%;
  transform-origin: left top;
  box-sizing: border-box;
  pointer-events: none;
  border-width1px;
  border-style: solid;
  border-color#333;
}
@media (-webkit-min-device-pixel-ratio2), (min-resolution2dppx) {
  .retina-border::before {
    width200%;
    height200%;
    transformscale(0.5);
  }
}
@media (-webkit-min-device-pixel-ratio3), (min-resolution3dppx) {
  .retina-border::before {
    width300%;
    height300%;
    transformscale(0.33);
  }
}
.normal-border {
  border1px solid #333;
}

具体实现

以两倍屏为例

.retina-border {
  position: relative;
}
.retina-border::before {
  content'';
  position: absolute;
  top0px;
  right0px;
  width200%;
  height200%;
  transformscale(0.5);
  transform-origin: left top;
  box-sizing: border-box;
  pointer-events: none;
  border-width1px;
  border-style: solid;
  border-color#333;
}

通过一个伪类选择器在retinaborder元素中加了一个子元素

border-width: 1px将边框的宽度设为1px。
width:200%然后将伪类元素的宽高都设置成父元素的2倍。(但是边框还是1px)
transform:scale(0.5)伪类元素的x,y轴方向都缩放到0.5倍。
通过两次尺寸的设置,使这个伪类子元素保持内容的大小还是和父元素一样,但是border:0.5px的效果。
pointer-events: none当有元素的层级重叠时,鼠标点击是无法穿透的。即绝对定位的伪类元素的层级更高,它底下的元素(即文字:retina border)无法被事件触发。置为none时,绝对定位的元素不触发事件,底下的那层才能被选中。

伪类元素默认的display:inline。而position:absolute会使元素display:block。只有块级元素的尺寸(宽/高)设置才是有效的。

其中伪类选择器中content是必填项,不然无法生效

transform-origin的缩放的中心点,默认是元素中心,

transform-origin的缩放的中心点,默认是元素中心,和绝对定位的top,right一样,相对的是padding+content部分整个空间的位置

绝对定位的元素其top和right值是相对于padding+content的,默认值是从content开始,所以要规定都是0,否则当父元素有padding时,border就移位了

(如果删去position:absolute)

(如果删去position:absolute+display:block)

当使用百分比时,其父元素的高度必须显式指定,(20px/20view)不能是由子元素撑开的,但是宽度是可以的。

两种方案比较

兼容性

svg方案经过postcss处理,最终会影响浏览器兼容性的是border-image属性

伪类元素元素:方案最终影响兼容性的是transform属性

结论:svg方案的兼容性更好

灵活性

由于svg只能画出特定的形状,所以无法实现圆角边框。而伪类元素方案可以。

学习成本

svg方案所用到的border-image属性、svg特性的理解成本较高,并且需要postcss-write-svg处理。伪类元素方案相较简单。

总结:通常情况,伪类元素方案更好,无论是从成本还是灵活性出发。
如果是为了更高的兼容性选择svg方案,border-image属性一定要使用缩写。(不然兼容性会更差兼容性测试