真.1px边框, 🚀 支持任意数量边和圆角, 1 个万金油的方法

15,601 阅读3分钟

相关源码: github.com/any86/5a.cs…

什么是"真.1px边框"?

🚀 由于现在手机都是"高密度屏幕", 我们写的1px到了手机上会用2/3个物理像素点去显示, 这样1px边框看起来就会比较粗, 所以如果你也觉得自己的1px边框比较粗, 那么请往下看.

================ 注意 ================

截图因为放大了可能不明显, 你可以用手机打开这2个网址对比效果非常明显:

================ 注意 ================

1种"万金油"的解决办法

其实如果你百度"1px边框"会有很多文章, 大部分都会说有7种方法, 不过其实经过我的测试, 其实我们只要会其中1种就够了, 下面就介绍下这个"万金油"的方法.

先说下原理

  1. 主要是通过给目标元素添加position:relative;
  2. 然后再用其伪类:after或者:before画一个2倍或者3倍宽高的元素;
  3. 然后给伪类元素画一个1px的边框;
  4. 通过media query决定缩放伪类元素到1/2或者1/3;
  5. 给伪类元素增加pointer-events: none;, 这样伪类元素可以点击穿透, 也就是能看到, 但是不触发任何默认事件(click等);

代码

函数签名

为了使用方便, 我把他封装成了scss的mixin.

@mixin thinBorder($directionMaps: bottom, $color: #ccc, $radius:(0, 0, 0, 0), $position: after)

说明下:

$directionMaps: 是个list类型可以传入多个方向, 也就是可以生成多个防线边框, 默认值为bottom, 你可以根据需要传入(top, left, bottom, right) 4个方向;

$color: 边框的颜色, 默认#ccc;

$radius: 圆角半径, 默认0;

$position是一个高级设置, 一般都不需要改动, 由于细边框的实现使用了css的伪类, 所以为了规避可能出现的样式冲突, 我们可以自己指定使用:after还是:before, 默认after;

🤖你可以直接复制下面的代码到你的项目使用

下面我们看下代码的实现:

@mixin thinBorder($directionMaps: bottom, $color: #ccc, $radius:(0, 0, 0, 0), $position: after) {
    // 是否只有一个方向
    $isOnlyOneDir: string==type-of($directionMaps);

    @if ($isOnlyOneDir) {
        $directionMaps: ($directionMaps);
    }

    @each $directionMap in $directionMaps {
        border-#{$directionMap}: 1px solid $color;
    }

    // 判断圆角是list还是number
    @if(list==type-of($radius)) {
        border-radius: nth($radius, 1) nth($radius, 2) nth($radius, 3) nth($radius, 4);
    }

    @else {
        border-radius: $radius;
    }

    @media only screen and (-webkit-min-device-pixel-ratio: 2) {
        & {
            position: relative;

            // 删除1像素密度比下的边框
            @each $directionMap in $directionMaps {
                border-#{$directionMap}: none;
            }
        }

        &:#{$position} {
            content: "";
            position: absolute;
            top: 0;
            left: 0;
            display: block;
            width: 200%;
            height: 200%;
            transform: scale(0.5);
            box-sizing: border-box;
            padding: 1px;
            transform-origin: 0 0;
            pointer-events: none;
            border: 0 solid $color;

            @each $directionMap in $directionMaps {
                border-#{$directionMap}-width: 1px;
            }

            // 判断圆角是list还是number
            @if(list==type-of($radius)) {
                border-radius: nth($radius, 1)*2 nth($radius, 2)*2 nth($radius, 3)*2 nth($radius, 4)*2;
            }

            @else {
                border-radius: $radius*2;
            }

        }
    }

    @media only screen and (-webkit-min-device-pixel-ratio: 3) {
        &:#{$position} {

            // 判断圆角是list还是number
            @if(list==type-of($radius)) {
                border-radius: nth($radius, 1)*3 nth($radius, 2)*3 nth($radius, 3)*3 nth($radius, 4)*3;
            }

            @else {
                border-radius: $radius*3;
            }

            width: 300%;
            height: 300%;
            transform: scale(0.3333);
        }
    }
}

使用

单侧边框

生成.border-top-1px等4个单侧边框;

@each $dir in (top,right,bottom,left) {
  .border-#{$dir}-#{1}px {
    @include thinBorder( $dir);
  }
}

多侧边框

生成"红色"的多侧边框.border-top-left-red-1px

  .border-top-left-red-1px{
    @include thinBorder((top,left), red);
  }

圆角边框

生成带100px圆角的边框 .border-top-left-round-1px

  .border-top-left-red-1px{
    @include thinBorder(top, red, 100px);
  }

使用:before去生成边框

  .border-top-before{
    @include thinBorder(top, red, 0, before);
  }

ios支持小数像素

其实ios8及其以上是支持小数像素的, 也可以实现细边框, 比如border-width:0.5px, 但是安卓不支持, 所以如果你只需要兼容ios, 其实可以直接用小数做单位. 但是我还是建议用上面的mixin, 毕竟省心效果也一样.

总结

上面封装的mixin兼容性非常好, 基本所有手机都兼容, 而且我还做了pc上的兼容, 请大家放心使用.

当然可能还有更好的方法是我不知道的, 如有还请大家多多指正留言, 感谢各位的阅读, 祝大家技术越来越强, 工资越来越多.

相关源码: github.com/any86/5a.cs…

微信群

感谢大家的阅读, 如有疑问可以加群🚀, 群里有好多有趣的前端的小伙伴, 让我们共同学习成长吧!

加我微信, 我拉你进入微信群(由于腾讯对微信群的100人限制, 超过100人后必须由我拉进去)