伪类元素方案
效果如下
// 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;
width: 100%;
height: 100%;
transform-origin: left top;
box-sizing: border-box;
pointer-events: none;
border-width: 1px;
border-style: solid;
border-color: #333;
}
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) {
.retina-border::before {
width: 200%;
height: 200%;
transform: scale(0.5);
}
}
@media (-webkit-min-device-pixel-ratio: 3), (min-resolution: 3dppx) {
.retina-border::before {
width: 300%;
height: 300%;
transform: scale(0.33);
}
}
.normal-border {
border: 1px solid #333;
}
具体实现
以两倍屏为例
.retina-border {
position: relative;
}
.retina-border::before {
content: '';
position: absolute;
top: 0px;
right: 0px;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: left top;
box-sizing: border-box;
pointer-events: none;
border-width: 1px;
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属性一定要使用缩写。(不然兼容性会更差兼容性测试)