关于移动开发1px边框的理解以及解决方案

前言

移动端web项目越来越多,在开发过程中经常遇到UI设计师对像素的要求越来越高,在 Retina屏幕中1px会比在web端要粗的多,因为css中的1px并不等于移动设备的1px,这是由于不同的手机有不同的像素密度照成的。

移动端 1px 像素问题是什么

举个例子,iphone6 的屏幕宽度为 375px,设计师做的视觉稿一般是 750px,也就是 2x 图,这个时候设计师在设计稿上画了 1px 的边框,如果你在 css 中也写了border-width: 1px,问题就出现了。 先来看一下出现的问题吧

  div {
        width: 200px;
        height: 200px;
    }

    .box1 {
        border-top: 1px solid #000;
    }

    .box2 {
        position: relative;
        border-top: 1px solid #000;
    }
    
<div class="box1">1px上边框</div>
<div class="box2">0.5px上边框</div>

    
复制代码

效果 :

image.png 那么为什么会发生这样的变化呢?

原因:

  • retina屏的手机上, dpr23css里写的1px宽度映射到物理像素上就有2px3px宽度。
  • iPhone6dpr2,物理像素是750(x轴),它的逻辑像素为375。也就是说,1个逻辑像素,在x轴和y轴方向,需要2个物理像素来显示,即:dpr=2时,表示1个CSS像素由4个物理像素点组成。

可能很多人就不明白了 dpr 是什么,物理像素是什么,逻辑像素又是什么

实际上 设备像素比:dpr=window.devicePixelRatio,也就是设备的物理像素与逻辑像素的比值

接下来就说一下什么是物理像素,什么是逻辑像素(枯燥乏味的知识)

物理像素

物理像素是指像素点的多少,而不是指具体的物理尺寸。显示屏是由一个个物理像素点组成的,通过控制每个像素点的颜色,使屏幕显示出不同的图像,屏幕从工厂出来那天起,它上面的物理像素点就固定不变了。

我们常说的分辨率指的就是物理像素,比如 iphone6s plus 的分辨率是 1920x1080,表示横向有 1920 个物理像素,竖向有 1080 个物理像素。

逻辑像素(设备独立像素)

设备独立像素是一个虚拟的单位,独立于设备,是用于逻辑上衡量长度的单位,由底层系统的程序使用,会由相关系统转换为物理像素。

一种形象的说法,可以理解为在 css 中的 px。设备独立像素 = 逻辑像素 = CSS 像素

设备独立像素有什么用?

举个例子,iphone3 和 iphone4 的尺寸都是 3.5 寸,但 iphone3 的分辨率是 320x480,iphone4 的分辨率是 640x960,这也就是意味着同样长度的屏幕,iphone3 有 320 个物理像素,iphone4 有 640 个物理像素。

如果我们按照真实的物理像素进行布局,比如说我们按照 320 物理像素进行布局,到了 640 物理像素的手机上就会有一半的空白,为了避免这种问题,就产生了虚拟像素单位(设备独立像素)。

我们统一 iphone3 和 iphone4 都是 320 个虚拟像素,只是在 iphone3 上,最终 1 个虚拟像素换算成 1 个物理像素,在 iphone4 中,1 个虚拟像素最终换算成 2 个物理像素。

至于 1 个虚拟像素被换算成几个物理像素,这个数值我们称之为设备像素比。

提示

iPhone6 分辨率 750 x 1334(物理像素),设备独立像素 375 x 667,设备像素比为 2。

iPhone6 plus 分辨率 1242 x 2208(物理像素),设备独立像素 414 x 736,设备像素比为 3。

解决方案

利用 css 的 伪元素::after + transfrom 进行缩放

实现全部边框

     /* 全部边框 */
    .box3 {
        position: relative;
    }

    .box3::after {
        content: '';
        position: absolute;
        box-sizing: border-box;
        top: 0;
        left: 0;
        width: 200%;
        height: 200%;
        border: 1px solid #000;
        border-radius: 4px;
        transform: scale(0.5);
        transform-origin: left top;
    }

    .box4 {
        margin-top: 20px;
        border: 1px solid #000;
    }
    
<div class="box3">0.5px全部边框</div>
<div class="box4"> 1px全部边框 </div>
    
复制代码

注意要加上这段代码:transform-origin: left top;,不然会出现虚化效果。

效果如下:

image.png

实现单上边框:

     .box2 {
        position: relative;
        /* border-top: 1px solid #000; */
    }

    /*  单个上边框 */
    .box2::after {
        content: '';
        box-sizing: border-box;
        position: absolute;
        top: 0;
        left: 0;
        width: 200px;
        height: 1px;
        background-color: #000;
        transform: scaleY(0.5);
        transform-origin: left top;
    }
    
<div class="box1">1px上边框</div>
<div class="box2">0.5px上边框</div>
复制代码

效果如下:

image.png

开源组件库解决方案

ant-design-mobile 组件库

@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;
}


@mixin hairline($direction, $color: #000, $radius: 0) {
  @if $direction == top {
    border-top: 1px solid $color;

    // min-resolution 用来检测设备的最小像素密度
    @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) {
      position: relative;
      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;
      }
    }
  }
}

// 移除边框
@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;
    }
  }
}
复制代码

vant

.hairline-common() {
  position: absolute;
  box-sizing: border-box;
  content: ' ';
  pointer-events: none;
}

.hairline(@color: @border-color) {
  .hairline-common();

  top: -50%;
  right: -50%;
  bottom: -50%;
  left: -50%;
  border: 0 solid @color;
  transform: scale(0.5);
}
复制代码

推荐

更多的细节可以看一下这位大佬的juejin.cn/post/684490…

猜你喜欢

转载自juejin.im/post/7083118017160151071
今日推荐