css实现1px的几种办法

css实现1px的几种办法

  • 背景
  • 物理像素和逻辑像素
  • 为什么css设置1px,但是在移动端上显示却感觉有些粗呢?
  • css如何实现1px的物理像素
    • 法一:利用box-shadow
    • 法二:设置 border-image 方案
    • 法三:使用background-image实现
    • 法四:多背景渐变实现
    • 法五:用小数来写px值
    • 法六:viewport + rem 实现
    • 法七:伪类 + transform 实现

背景

物理像素和逻辑像素

  • 物理像素(physical pixel)
    物理像素也叫硬件像素或者设备像素,一个物理像素是显示器(手机屏幕)上最小的物理显示单元(像素颗粒),在操作系统的调度下,每一个设备像素都有自己的颜色值和亮度值。 如:iPhone6上就有750*1334个物理像素颗粒。

  • 逻辑像素(density-independent pixel)
    逻辑像素也叫设备独立像素或者密度无关像素,可以认为是计算机坐标系统中得一个点,这个点代表一个可以由程序使用的虚拟像素(比如:css像素。然后由相关系统转换为物理像素。所以说,物理像素和设备独立像素之间存在着一定的对应关系,这就是接下来要说的设备像素比。

(3)设备像素比(device pixel ratio )简称dpr
设备像素比(简称dpr)定义了物理像素和设备独立像素的对应关系。它的值可以按如下的公式的得到:

设备像素比(dpr)=物理像素/逻辑像素(px) // 在某一方向上,x方向或者y方向,下图dpr=2
css实现1px的几种办法_第1张图片

为什么css设置1px,但是在移动端上显示却感觉有些粗呢?

知道了设备像素比,我们就大概知道了1px线变粗的原因。简单来说就是手机屏幕分辨率越来越高了,同样屏幕大小的一个手机,它的实际物理像素数更多了。因为不同的移动设备有不同的像素密度,所以我们所写的1px在不同的移动设备上展示是不一样的。

  • 先从一个例子来看:
    iPhone 3GS 和 iPhone 4 的像素分别是 320px 和 640px,但是显示屏的宽度都是相同的,所以为了在所有设备上渲染出的显示效果相同,CSS 中的 1px 映射到 iPhone 4 的物理像素上,就会是 2px。同样的道理,在 iPhone 5、6 上 CSS 的 1px 对应物理像素 2px,6plus 则是 3px。所以当我们设置 1px 时,实际的显示效果其实是由两个甚至三个像素点所绘制的。

  • 现在做移动端开发时一般都要加上一句话:

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    
    

    这句话定义了本页面的viewport的宽度为设备宽度,初始缩放值和最大缩放值都为1,并禁止了用户缩放。

    手机存在一个能完美适配的理想viewport, 分辨率相差很大的手机的理想viewport的宽度可能是一样的, 这样做的目的是为了保证同样的css在不同屏幕下的显示效果是一致的, viewport的好处就在于一套css可以适配多个机型。

    在window对象中有一个devicePixelRatio属性,他可以反应css中的像素与设备的像素比。然而1px在不同的移动设备上都等于这个移动设备的1px,这是因为不同的移动设备有不同的像素密度。有关这个属性,它的官方的定义为:设备物理像素和设备独立像素(逻辑像素)的比例,也就是

    devicePixelRatio = 物理像素 / 设备独立像素
    
  • 1px变粗的原因:
    viewport的物理像素和逻辑像素(css像素)是按比例而不是相同的. 移动端window对象有个devicePixelRatio属性, 它表示设备物理像素和css像素的比例, 在retina屏的iphone手机上, 这个值为2或3, css里写的1px长度映射到物理像素上就有2px或3px那么长。通过设置viewport,可以改变css中的1px用多少物理像素来渲染,设置了不同的viewport,当然1px的线条看起来粗细不一致。

    其实出现1px的原因还是在于,UI设计师要求的1px是物理像素,而我们开发写的css是逻辑像素,他们是不一样的,存在一个换算比例,通常JavaScript可以通过window.devicePixelRatio来获取,在iPhone上出现边框变宽原因就是因为devicePixelRatio=2,而border-width=1,边框被放大了俩倍,导致出现边框变宽。

css如何实现1px的物理像素

法一:利用box-shadow

  • 优点:
    代码量少
    可以满足所有场景
  • 缺点:
    边框有阴影,颜色变浅
    css实现1px的几种办法_第2张图片
    css实现1px的几种办法_第3张图片
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>实现1px的解决办法</title>
  <style>
    .box {
      width: 100px;
      height: 100px;
      margin: 50px auto;
      background-color: antiquewhite;
      box-shadow: 0px 1px 0px 0px rgba(0, 0, 0, 1);
    }
  </style>
</head>
<body>
  <div class="box"></div>
</body>
</html>

效果
css实现1px的几种办法_第4张图片
将阴影尺寸设置为负数,设置成-1px 是为了让阴影尺寸稍小于div元素尺寸,这样左右两边的阴影就不会暴露出来,实现只有底部一边有阴影的效果。从而实现分割线效果(单边边框)

法二:设置 border-image 方案

用1px宽度图片做border图片

  • 优点:
    可以设置单条,多条边框
    没有性能瓶颈的问题
  • 缺点:
    修改颜色麻烦, 需要替换图片
    圆角需要特殊处理,并且边缘会模糊
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>实现1px的解决办法</title>
  <style>
    .box {
      width: 100px;
      height: 100px;
      margin: 50px auto;
      background-color: antiquewhite;
    }
    .border-image-1px {
      border-bottom: 1px solid transparent;
      border-image: url(border.png) 30 stretch;
    }
  </style>
</head>
<body>
  <div class="box border-image-1px"></div>
</body>
</html>

css实现1px的几种办法_第5张图片

法三:使用background-image实现

background-image 跟border-image的方法一样,你要先准备一张符合你要求的图片。然后将边框模拟在背景上。

  • 优点:
    可以设置单条,多条边框
    没有性能瓶颈的问题
  • 缺点:
    修改颜色麻烦, 需要替换图片
    圆角需要特殊处理,并且边缘会模糊
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>实现1px的解决办法</title>
  <style>
    .box {
      width: 100px;
      height: 100px;
      margin: 50px auto;
      background-color: antiquewhite;
    }

    .background-image-1px {
      background: url(border.png) repeat-x left bottom;
      -webkit-background-size: 100% 1px;
      background-size: 100% 1px;
      background-color: antiquewhite;
    }
  </style>
</head>
<body>
  <div class="box background-image-1px"></div>
</body>
</html>

法四:多背景渐变实现

与background-image方案类似,只是将图片替换为css3渐变。设置1px的渐变背景,50%有颜色,50%透明。

  • 优点:
    可以实现单条、多条边框
    边框的颜色随意设置
  • 缺点:
    代码量不少
    圆角没法实现
    多背景图片有兼容性问题
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>实现1px的解决办法</title>
  <style>
    .background-gradient-1px {
      background:
        linear-gradient(#000, #000 100%, transparent 100%) left / 1px 100% no-repeat,
        linear-gradient(#000, #000 100%, transparent 100%) right / 1px 100% no-repeat,
        linear-gradient(#000,#000 100%, transparent 100%) top / 100% 1px no-repeat,
        linear-gradient(#000,#000 100%, transparent 100%) bottom / 100% 1px no-repeat
    }

    .box {
      width: 100px;
      height: 100px;
      margin: 50px auto;
      background-color: antiquewhite;
    }
  </style>
</head>
<body>
  <div class="box background-gradient-1px"></div>
</body>
</html>

css实现1px的几种办法_第6张图片

法五:用小数来写px值

IOS8下已经支持带小数的px值, media query对应devicePixelRatio有个查询值-webkit-min-device-pixel-ratio, css可以写成这样

.border { border: 1px solid #999 }
@media screen and (-webkit-min-device-pixel-ratio: 2) {
    .border { border: 0.5px solid #999 }
}
@media screen and (-webkit-min-device-pixel-ratio: 3) {
    .border { border: 0.333333px solid #999 }
}
  • 优点:
    简单,不需要过多代码。
  • 缺点:
    无法兼容安卓设备、 iOS 8 以下设备。

法六:viewport + rem 实现

整体思路:是在viewport设置缩放,通过js去动态修改viewport的值。

  • 优点:
    所有场景都能满足
    一套代码,可以兼容基本所有布局
  • 缺点:
    老项目修改代价过大,只适用于新项目

在页面初始化的时候,设置viewport:

<meta name="viewport" id="WebViewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">

通过js来动态实现对viewport的修改:

var viewport = document.querySelector("meta[name=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.333333333, maximum-scale=0.333333333, minimum-scale=0.333333333, user-scalable=no')
} 
var docEl = document.documentElement;
var fontsize = 10 * (docEl.clientWidth / 320) + 'px';
docEl.style.fontSize = fontsize;

法七:伪类 + transform 实现

原理是把原先元素的 border 去掉,然后利用 :before 或者 :after 重做 border ,边框宽度设置为1px,并 transform 的 scale 缩小一半,原先的元素相对定位,新做的 border 绝对定位。

  • 优点:
    所有场景都能满足
    支持圆角(伪类和本体类都需要加border-radius)
  • 缺点:
    需要注意是没有:before, :after伪元素的
    对于已经使用伪类的元素(例如clearfix),可能需要多层嵌套

单条border样式设置:

.scale-1px{
  position: relative;
  border:none;
}
.scale-1px:after{
  content: '';
  position: absolute;
  bottom: 0;
  background: #000;
  width: 100%;
  height: 1px;
  -webkit-transform: scaleY(0.5);
  transform: scaleY(0.5);
  -webkit-transform-origin: 0 0;
  transform-origin: 0 0;
}

四条boder样式设置:

.scale-1px{
  position: relative;
  margin-bottom: 20px;
  border:none;
}
.scale-1px:after{
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  border: 1px solid #000;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  width: 200%;
  height: 200%;
  -webkit-transform: scale(0.5);
  transform: scale(0.5);
  -webkit-transform-origin: left top;
  transform-origin: left top;
}

最好在使用前也判断一下,结合 JS 代码,判断是否 Retina 屏:

if(window.devicePixelRatio && devicePixelRatio >= 2){
  document.querySelector('ul').className = 'scale-1px';
}

文章参考:
7种方法解决移动端Retina屏幕1px边框问题
移动端1px细线解决方案总结
css基础之移动端1px问题
移动端1px模糊产生的原因以及解决方案
css实现1px 像素线条_解决移动端1px线条的显示方式
关于移动端开发1px线的一些理解和解决办法
移动端1px误差的原因以及解决方案
CSS 实现移动端 1px 线条的绘制

你可能感兴趣的:(前端面试题汇总,css)