移动端 1px

768 阅读5分钟

基本概念

物理像素、设备独立像素、CSS像素、分辨率、PPI、devicePixelRatio 区别

  • 物理像素,又叫设备像素
    • 显示器的最小物理单位。其尺寸大小是不会变的,从显示屏从工厂出来的那刻起,物理像素点就不会变了。
    • iPhone 6 的分辨率是750 x 1334 ,那么这个 750 就是代表 750 的物理像素,是从手机出厂的那刻起,就不会变了,750 表示的就是手机的宽是 750px。这个应该比较好理解。
  • 设备独立像素
    • 设备独立像素又称逻辑像素,其尺寸大小是相对的。是一种物理测量单位,基于计算机控制的坐标系统和抽象像素。
    • 一个设备独立像素里可能包含1个或者多个物理像素点,包含的越多则屏幕看起来越清晰。
  • 像素分辨率
    • 以手机屏幕为例,iphonex像素分辨率为1125x2436,是指屏幕横向能显示1125个物理像素点,纵向能显示2436个物理像素点。通常说的4K显示屏指的是 4096x2160。
  • PPI
    • 每英寸的物理像素数。以尺寸为5.8英寸(屏幕对角线长度)、分辨率为1125x2436的iphonex为例, ppi = Math.sqrt(1125*1125 + 2436*2436) / 5.8 ,值为 463ppi
  • CSS 像素
    • 像素是一个单位,而不是一个点
  • devicePixelRatio
    • window.devicePixelRatio 指的是设备物理像素和设备独立像素(device-independent pixels, dips)的比例。window.devicePixelRatio = 物理像素 / 设备独立像素(dips) 。经计算, iphone x的 devicePixelRatio 是3。

尺寸的区别

获取屏幕尺寸( 设备独立像素值 ):

screen.width
screen.height

image.png

如:iPhoneX: 375 * 812 获取窗口尺寸(css像素) 如:iPhoneX: 375 * 812 包含滚动条

window.innerWidth
window.innerHeight

不包含滚动条

document.documentElement.clientWidth
document.documentElement.clientHeight 

取html元素尺寸(内容):

document.documentElement.offsetWidth 
document.documentElement.offsetHight

1px

高清屏下,移动端的1px 会很粗。 比如,这个是假的1像素

这个是真的1像素

产生原因

那么为什么会产生这个问题呢?主要是跟一个东西有关,DPR(devicePixelRatio) 设备像素比,它是默认缩放为100%的情况下,设备像素和CSS像素的比值。

window.devicePixelRatio=物理像素 /CSS像素

目前主流的屏幕DPR=2 (iPhone 8),或者3 (iPhone 8 Plus)。拿2倍屏来说,设备的物理像素要实现1像素,而DPR=2,所以css 像素只能是 0.5。一般设计稿是按照750来设计的,它上面的1px是以750来参照的,而我们写css样式是以设备375为参照的,所以我们应该写的0.5px就好了啊! 试过了就知道,iOS 8+系统支持,安卓系统不支持。

解决方案

1、WWDC对iOS统给出的方案

推荐指数:**

在 WWDC大会上,给出来了1px方案,当写 0.5px的时候,就会显示一个物理像素宽度的 border,而不是一个css像素的 border。 所以在iOS下,你可以这样写。

border:0.5px solid #E5E5E5

可能你会问为什么在3倍屏下,不是0.3333px 这样的?经过我测试,在Chrome上模拟iPhone 8Plus,发现小于0.46px的时候是显示不出来。

总结:

  • 优点:简单,没有副作用
  • 缺点:支持iOS 8+,不支持安卓。后期安卓follow就好了。

2、使用边框图片

推荐指数:**

border: 1px solid transparent;
border-image: url('./../../image/96.jpg') 2 repeat;

图片的颜色就是此后border的颜色

这个方法在W3CPlus 上的例子讲的非常细致 www.w3cplus.com/content/css…

总结:

  • 优点:没有副作用
  • 缺点:border颜色变了就得重新制作图片;圆角会比较模糊。

3、使用box-shadow实现

推荐指数:***

先复习一下box-shadow,看一下MDN 上的这篇就够了developer.mozilla.org/zh-CN/docs/… 。 再看一下效果

代码是怎样实现的呢?

box-shadow: 0  -1px 1px -1px #e5e5e5,   //上边线
            1px  0  1px -1px #e5e5e5,   //右边线
            0  1px  1px -1px #e5e5e5,   //下边线
            -1px 0  1px -1px #e5e5e5;   //左边线

前面两个值 x,y 主要控制显示哪条边,后面两值控制的是阴影半径、扩展半径。 其实方法可以到这个地址线上尝试一下

总结

  • 优点:使用简单,圆角也可以实现
  • 缺点:模拟的实现方法,仔细看谁看不出来这是阴影不是边框。

4、使用伪元素

推荐指数:****

这个方法是我使用最多的,做出来的效果也是非常棒的,直接上代码。

1 条border

.setOnePx{
  position: relative;
  &::after{
    position: absolute;
    content: '';
    background-color: #e5e5e5;
    display: block;
    width: 100%;
    height: 1px; /*no*/
    transform: scale(1, 0.5);
    top: 0;
    left: 0;
  }
}

可以看到,将伪元素设置绝对定位,并且和父元素的左上角对齐,将width 设置100%,height设置为1px,然后进行在Y方向缩小0.5倍

4 条border

```
.setBorderAll{
     position: relative;
       &:after{
           content:" ";
           position:absolute;
           top: 0;
           left: 0;
           width: 200%;
           height: 200%;
           transform: scale(0.5);
           transform-origin: left top;
           box-sizing: border-box;
           border: 1px solid #E5E5E5;
           border-radius: 4px;
      }
    }
```

同样为伪元素设置绝对定位,并且和父元素左上角对其。将伪元素的长和宽先放大2倍,然后再设置一个边框,以左上角为中心,缩放到原来的`0.5倍` 

总结:

  • 优点:全机型兼容,实现了真正的1px,而且可以圆角。
  • 缺点:暂用了after 伪元素,可能影响清除浮动。

5、设置viewport的scale值

推荐指数:*****

这个解决方案是利用viewport+rem+js 实现的。 参考了网上的一个例子 移动端1像素边框问题的解决方案,自己手动实现了一下。

效果不错。 上代码

<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>

总结

  • 优点:全机型兼容,直接写1px不能再方便
  • 缺点:适用于新的项目,老项目可能改动大

总结

总结一下,新项目最好使用的是设置viewport的scale值,这个方法兼容性好,后期写起来方便。老项目的话,改起来可能比较多,用的比较多的方法就是伪元素+transform的方法。 其他的背景图片,阴影的方法毕竟还是不太灵活,而且兼容性不好。

原文