移动端1像素边框问题

文章介绍了一种使用Antd库结合CSS的伪元素和transform缩放技术,来确保在不同设备物理像素比(dpr)下,1px边框能保持一致的显示效果。通过媒体查询动态调整伪元素的transform属性,实现了在2dppx和3dppx设备上的精细调整。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在移动端不同设备的dpr(物理像素比)不一样,导致1px的逻辑像素渲染出不同的粗细,比如dpr=3时1个逻辑像素点(1px)=3个物理像素点 这不是我们想要的 我们要在所有不同dpr的设备上显示1px的效果保持一致

最佳实践: antd的伪元素+transform缩放实现:

src/styles/common.scss:

// 设置边框的公共方法
@mixin scale-hairline-common($color, $top, $right, $bottom, $left) {
  content: '';
  position: absolute;
  display: block;
  z-index: 1;
  top: $top;
  right: $right;
  bottom: $bottom;
  left: $left;
  background-color: $color;
}

// 添加边框
/* 
        用法:
      
        // 导入(已全局导入 这步可省略)
        @import '@scss/hairline.scss';
      
        // 在类中使用
        .a {
          @include hairline(bottom, #f0f0f0);
        }
      */
@mixin hairline($direction, $color: #000, $radius: 0) {
  position: relative; // 父盒子相对定位 否则伪元素会跑到别的定位元素去
  @if $direction == top {
    border-top: 1px solid $color;

    // min-resolution 用来检测设备的最小像素密度
    /**
      因为dpr不一样导致1px所占的物理像素个数不一样导致线条变粗 根据像素比等比缩放
      如果设备像素比不为1则用scale等比缩放到1 否则直接设置border为1px
    */
    @media (min-resolution: 2dppx) {
      border-top: none;

      &::before {
        @include scale-hairline-common($color, 0, auto, auto, 0);
        width: 100%;
        height: 1px;
        transform-origin: 50% 50%;
        transform: scaleY(0.5);

        @media (min-resolution: 3dppx) {
          transform: scaleY(0.33);
        }
      }
    }
  } @else if $direction == right {
    border-right: 1px solid $color;

    @media (min-resolution: 2dppx) {
      border-right: none;

      &::after {
        @include scale-hairline-common($color, 0, 0, auto, auto);
        width: 1px;
        height: 100%;
        background: $color;
        transform-origin: 100% 50%;
        transform: scaleX(0.5);

        @media (min-resolution: 3dppx) {
          transform: scaleX(0.33);
        }
      }
    }
  } @else if $direction == bottom {
    border-bottom: 1px solid $color;

    @media (min-resolution: 2dppx) {
      border-bottom: none;

      &::after {
        @include scale-hairline-common($color, auto, auto, 0, 0);
        width: 100%;
        height: 1px;
        transform-origin: 50% 100%;
        transform: scaleY(0.5);

        @media (min-resolution: 3dppx) {
          transform: scaleY(0.33);
        }
      }
    }
  } @else if $direction == left {
    border-left: 1px solid $color;

    @media (min-resolution: 2dppx) {
      border-left: none;

      &::before {
        @include scale-hairline-common($color, 0, auto, auto, 0);
        width: 1px;
        height: 100%;
        transform-origin: 100% 50%;
        transform: scaleX(0.5);

        @media (min-resolution: 3dppx) {
          transform: scaleX(0.33);
        }
      }
    }
  } @else if $direction == all {
    border: 1px solid $color;
    border-radius: $radius;

    @media (min-resolution: 2dppx) {
      border: none;

      &::before {
        content: '';
        position: absolute;
        left: 0;
        top: 0;
        width: 200%;
        height: 200%;
        border: 1px solid $color;
        border-radius: $radius * 2;
        transform-origin: 0 0;
        transform: scale(0.5);
        box-sizing: border-box;
        pointer-events: none;
        @media (min-resolution: 3dppx) {
          width: 300%;
          height: 300%;
          transform: scale(0.33);
          border-radius: $radius * 3;
        }
      }
    }
  }
}

// 移除边框
@mixin hairline-remove($position: all) {
  @if $position == left {
    border-left: 0;
    &::before {
      display: none !important;
    }
  } @else if $position == right {
    border-right: 0;
    &::after {
      display: none !important;
    }
  } @else if $position == top {
    border-top: 0;
    &::before {
      display: none !important;
    }
  } @else if $position == bottom {
    border-bottom: 0;
    &::after {
      display: none !important;
    }
  } @else if $position == all {
    border: 0;
    &::before {
      display: none !important;
    }
    &::after {
      display: none !important;
    }
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值