美文网首页
移动端1px问题解决方案

移动端1px问题解决方案

作者: 东西里 | 来源:发表于2020-07-19 18:16 被阅读0次

    这是我第25篇简书。

    一、产生原因

    DPR(devicePixelRatio) 设备像素比 = 物理像素 / css像素 ,它是默认缩放为100%的屏幕下,设备像素和css像素的比值。
    目前比较主流的设备的DPR=2或3,所以:
    当我们的DPR为2,也就是2倍屏时,当物理像素(设备像素)为1px的时候,我们的css像素应该是0.5px。
    当DPR=3,物理像素为1px时,css像素应该为1/3px。
    而一般情况下,设计稿是按照750来设计的,而我们写css的样式是参考375的屏,所以我们写的css像素应该为设计稿的像素的一半。
    设计图为1px,我们css像素应该为0.5px,再考虑到设备的像素比,我们写的css则是0.25px。

    二、解决方案

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

    • 优点:全机型兼容,实现了真正的1px,而且可以圆角。适用于老项目。

    • 缺点:暂用了after 伪元素,可能影响清除浮动。

    <div class="cell border-1px"> cell <div>
    
    <style>
    .cell {
        width: 100px;
        height: 100px;
    }
    <!--全部边框-->
    .border-1px:after {
        content: '';
        position: absolute;
        box-sizing: border-box;
        top: 0;
        left: 0;
        width: 200%;
        height: 200%;
        border: 1px solid #000;
        border-radius: 4px;
        -webkit-transform: scale(0.5);
        transform: scale(0.5);
        -webkit-transform-origin: top left;
    }
    
    <!--单边框,以上边框为例-->
    .border-1px-top:before {
        content: "";
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        border-top: 1px solid red;
        transform: scaleY(.5);
        transform-origin: left top;
    }
    </style>
    

    (二)设置viewport的scale值

    • 优点:全机型兼容,直接写1px不能再方便。

    • 缺点:适用于新的项目,老项目可能改动大。

    <html>
      <head>
          <title>1px question</title>
          <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
          <meta name="viewport" id="WebViewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">        
          <style>
              html {
                  font-size: 1px;
              }            
              * {
                  padding: 0;
                  margin: 0;
              }
              .top_b {
                  border-bottom: 1px solid #E5E5E5;
              }
     
              .a,.b {
                          box-sizing: border-box;
                  margin-top: 1rem;
                  padding: 1rem;                
                  font-size: 1.4rem;
              }
     
              .a {
                  width: 100%;
              }
     
              .b {
                  background: #f5f5f5;
                  width: 100%;
              }
          </style>
          <script>
              var viewport = document.querySelector("meta[name=viewport]");
              //下面是根据设备像素设置viewport
              if (window.devicePixelRatio == 1) {
                  viewport.setAttribute('content', 'width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no');
              }
              if (window.devicePixelRatio == 2) {
                  viewport.setAttribute('content', 'width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no');
              }
              if (window.devicePixelRatio == 3) {
                  viewport.setAttribute('content', 'width=device-width,initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no');
              }
              var docEl = document.documentElement;
              var fontsize = 32* (docEl.clientWidth / 750) + 'px';
              docEl.style.fontSize = fontsize;
          </script>
      </head>
      <body>
          <div class="top_b a">下面的底边宽度是虚拟1像素的</div>
          <div class="b">上面的边框宽度是虚拟1像素的</div>
      </body>
    </html>
    

    (三)通过@media手写border

    .min-device-pixel-ratio(@scale2, @scale3) {
      @media screen and (min-device-pixel-ratio: 2), (-webkit-min-device-pixel-ratio: 2) {
        transform: @scale2;
      }
      @media screen and (min-device-pixel-ratio: 3), (-webkit-min-device-pixel-ratio: 3) {
        transform: @scale3;
      }
    }
     
    .border-1px(@color: #DDD, @radius: 2PX, @style: solid) {
      &::before {
        content: "";
        pointer-events: none;
        display: block;
        position: absolute;
        left: 0;
        top: 0;
        transform-origin: 0 0;
        border: 1PX @style @color;
        border-radius: @radius;
        box-sizing: border-box;
        width: 100%;
        height: 100%;
        @media screen and (min-device-pixel-ratio: 2), (-webkit-min-device-pixel-ratio: 2) {
          width: 200%;
          height: 200%;
          border-radius: @radius * 2;
          transform: scale(.5);
        }
        @media screen and (min-device-pixel-ratio: 3), (-webkit-min-device-pixel-ratio: 3) {
          width: 300%;
          height: 300%;
          border-radius: @radius * 3;
          transform: scale(.33);
        }
      }
    }
     
    .border-top-1px(@color: #DDD, @style: solid) {
      &::before {
        content: "";
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        border-top: 1Px @style @color;
        transform-origin: 0 0;
        .min-device-pixel-ratio(scaleY(.5), scaleY(.33));
      }
    }
    .border-bottom-1px(@color: #DDD, @style: solid) {
      &::after {
        content: "";
        position: absolute;
        left: 0;
        bottom: 0;
        width: 100%;
        border-bottom: 1Px @style @color;
        transform-origin: 0 0;
        .min-device-pixel-ratio(scaleY(.5), scaleY(.33));
      }
    }
    .border-left-1px(@color: #DDD, @style: solid) {
      &::before {
        content: "";
        position: absolute;
        left: 0;
        top: 0;
        height: 100%;
        border-left: 1Px @style @color;
        transform-origin: 0 0;
        .min-device-pixel-ratio(scaleX(.5), scaleX(.33));
      }
    }
    

    一般情况下,我们采用移动端新写的项目都采用viewport的scale方法来实现1px的边框,兼容性好。老项目的话建议还是采用伪元素加transform这种方式。

    开源库的解决方案:
    如果有用这些ui库,则不用考虑1px问题了。

    (四)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);
    }
    

    (五)ant-design-mobile 组件库

    这里 PX 大写,为了防止插件将 px 转成 rem 等单位:

    .scale-hairline-common(@color, @top, @right, @bottom, @left) {
      content: '';
      position: absolute;
      background-color: @color;
      display: block;
      z-index: 1;
      top: @top;
      right: @right;
      bottom: @bottom;
      left: @left;
    }
    
    .hairline(@direction, @color: @border-color-base) when (@direction = 'top') {
      border-top: 1PX solid @color;
    
      html:not([data-scale]) & {
        @media (min-resolution: 2dppx) {
          border-top: none;
    
          &::before {
            .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);
            }
          }
        }
      }
    }
    
    .hairline(@direction, @color: @border-color-base) when (@direction = 'right') {
      border-right: 1PX solid @color;
    
      html:not([data-scale]) & {
        @media (min-resolution: 2dppx) {
          border-right: none;
    
          &::after {
            .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);
            }
          }
        }
      }
    }
    .hairline(@direction, @color: @border-color-base) when (@direction = 'bottom') {
      border-bottom: 1PX solid @color;
      html:not([data-scale]) & {
        @media (min-resolution: 2dppx) {
          border-bottom: none;
          &::after {
            .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);
            }
          }
        }
      }
    }
    
    .hairline(@direction, @color: @border-color-base) when (@direction = 'left') {
      border-left: 1PX solid @color;
    
      html:not([data-scale]) & {
        @media (min-resolution: 2dppx) {
          border-left: none;
    
          &::before {
            .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);
            }
          }
        }
      }
    }
    
    .hairline(@direction, @color: @border-color-base, @radius: 0) when (@direction = 'all') {
      border: 1PX solid @color;
      border-radius: @radius;
    
      html:not([data-scale]) & {
        @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;
    
            // @media (min-resolution: 3dppx) {
            //   width: 300%;
            //   height: 300%;
            //   border-radius: @radius * 3;
            //   transform: scale(0.33);
            // }
          }
        }
      }
    }
    

    相关文章

      网友评论

          本文标题:移动端1px问题解决方案

          本文链接:https://www.haomeiwen.com/subject/mdaxkktx.html