css实现1px的几种办法
背景
物理像素和逻辑像素
-
物理像素(physical pixel)
物理像素也叫硬件像素或者设备像素,一个物理像素是显示器(手机屏幕)上最小的物理显示单元(像素颗粒),在操作系统的调度下,每一个设备像素都有自己的颜色值和亮度值。 如:iPhone6上就有750*1334个物理像素颗粒。 -
逻辑像素(density-independent pixel)
逻辑像素也叫设备独立像素或者密度无关像素,可以认为是计算机坐标系统中得一个点,这个点代表一个可以由程序使用的虚拟像素(比如:css像素。然后由相关系统转换为物理像素。所以说,物理像素和设备独立像素之间存在着一定的对应关系,这就是接下来要说的设备像素比。
(3)设备像素比(device pixel ratio )简称dpr
设备像素比(简称dpr)定义了物理像素和设备独立像素的对应关系。它的值可以按如下的公式的得到:
设备像素比(dpr)=物理像素/逻辑像素(px) // 在某一方向上,x方向或者y方向,下图dpr=2
为什么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
- 优点:
代码量少
可以满足所有场景 - 缺点:
边框有阴影,颜色变浅
<!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>
效果
将阴影尺寸设置为负数,设置成-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>
法三:使用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>
法五:用小数来写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) - 缺点:
需要注意<input type=“button”>是没有: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 线条的绘制