背景
物理像素和逻辑像素
物理像素(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 时,实际的显示效果其实是由两个甚至三个像素点所绘制的。
现在做移动端开发时一般都要加上一句话:
<metaname="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
- 优点:
代码量少
可以满足所有场景 - 缺点:
边框有阴影,颜色变浅
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>实现1px的解决办法</title><style> .box { width: 100px; height: 100px; margin: 50pxauto; background-color: antiquewhite; box-shadow: 0px1px0px0pxrgba(0, 0, 0, 1); } </style></head><body><divclass="box"></div></body></html>
效果
将阴影尺寸设置为负数,设置成-1px 是为了让阴影尺寸稍小于div元素尺寸,这样左右两边的阴影就不会暴露出来,实现只有底部一边有阴影的效果。从而实现分割线效果(单边边框)
法二:设置 border-image 方案
用1px宽度图片做border图片
优点:
可以设置单条,多条边框
没有性能瓶颈的问题
缺点:
修改颜色麻烦, 需要替换图片
圆角需要特殊处理,并且边缘会模糊
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>实现1px的解决办法</title><style> .box { width: 100px; height: 100px; margin: 50pxauto; background-color: antiquewhite; } .border-image-1px { border-bottom: 1pxsolidtransparent; border-image: url(border.png) 30stretch; } </style></head><body><divclass="box border-image-1px"></div></body></html>
法三:使用background-image实现
background-image 跟border-image的方法一样,你要先准备一张符合你要求的图片。然后将边框模拟在背景上。
优点:
可以设置单条,多条边框
没有性能瓶颈的问题
缺点:
修改颜色麻烦, 需要替换图片
圆角需要特殊处理,并且边缘会模糊
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>实现1px的解决办法</title><style> .box { width: 100px; height: 100px; margin: 50pxauto; background-color: antiquewhite; } .background-image-1px { background: url(border.png) repeat-xleftbottom; -webkit-background-size: 100%1px; background-size: 100%1px; background-color: antiquewhite; } </style></head><body><divclass="box background-image-1px"></div></body></html>
法四:多背景渐变实现
与background-image方案类似,只是将图片替换为css3渐变。设置1px的渐变背景,50%有颜色,50%透明。
- 优点:
可以实现单条、多条边框
边框的颜色随意设置 - 缺点:
代码量不少
圆角没法实现
多背景图片有兼容性问题
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="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: 50pxauto; background-color: antiquewhite; } </style></head><body><divclass="box background-gradient-1px"></div></body></html>
法五:用小数来写px值
IOS8下已经支持带小数的px值, media query对应devicePixelRatio有个查询值-webkit-min-device-pixel-ratio, css可以写成这样
.border { border: 1pxsolid#999 }@mediascreenand (-webkit-min-device-pixel-ratio: 2) { .border { border: 0.5pxsolid#999 }} @mediascreenand (-webkit-min-device-pixel-ratio: 3) { .border { border: 0.333333pxsolid#999 }}
优点:
简单,不需要过多代码。
缺点:
无法兼容安卓设备、 iOS 8 以下设备。
法六:viewport + rem 实现
整体思路:是在viewport设置缩放,通过js去动态修改viewport的值。
优点:
所有场景都能满足
一套代码,可以兼容基本所有布局
缺点:
老项目修改代价过大,只适用于新项目
在页面初始化的时候,设置viewport:
<metaname="viewport"id="WebViewport"content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
通过js来动态实现对viewport的修改:
varviewport=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') } vardocEl=document.documentElement; varfontsize=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: 00; transform-origin: 00; }
四条boder样式设置:
.scale-1px{ position: relative; margin-bottom: 20px; border:none; } .scale-1px:after{ content: ''; position: absolute; top: 0; left: 0; border: 1pxsolid#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: lefttop; transform-origin: lefttop; }
最好在使用前也判断一下,结合 JS 代码,判断是否 Retina 屏:
if(window.devicePixelRatio&&devicePixelRatio>=2){ document.querySelector('ul').className='scale-1px'; }