在移动端web开发中,UI设计稿中设置边框为1像素,前端在开发过程中如果出现border:1px,测试会发现在某些机型上,1px会比较粗,即是较经典的移动端1px像素问题。首选先看一下,pc时代和移动端时代对1px的对比。
一、像素的理解
像素是网页布局的基础。一个像素就是计算机能够显示一种特定颜色的最小区域。当设备尺寸相同但像素变得更密集时,屏幕能显示的画面的过渡更细致,网站看起来更明快。
1、device pixels
设备像素:显示屏幕的最小物理单位,每个dp包含自己的颜色、高宽等,不可再细分。设备像素是在设备出厂是设定的,设备一旦造出来就不会变大小和数量。官方在产品说明书上写的1920x1080就是说的物理像素。
2、dpi
设备独立像素:dpi(Dots Per Inch,每英寸点数)是一个量度单位,指每一英寸长度中,取样、可显示或输出点的数目。每英寸的像素,类似于密度,即每英寸的像素点数量。
3、css pixels
就是CSS和JS所理解的像素单位,它跟设备屏幕的像素没必然关系,比如windows的桌面显示器,当你修改显示器的硬件分辨率,比如把1920的分辨率改成1024分辨率,你会发现网页里的图形和字体变得很大很大的,同样的显示器,原本能显示全部网页,现在只能显示一半宽度,也就是说CSS像素变大了。所以,CSS像素是可以被硬件和软件任意调节的单位。css像素是独立于设备逻辑的,用于逻辑上衡量像素的单位。CSS声明和几乎所有的javascript属性都使用CSS像素。
//我们通过CSS和javascript代码设置的像素都是逻辑像素
width:250px;
font-size:22px;
1
2
3
4、dpr
设备像素比dpr = 设备像素 / CSS像素(某一方向上)
可以通过window.devicePixelRatio获取设备的dpr值。一般来说,在桌面的浏览器中,设备像素比(dpr)等于1,一个css像素就是代表的一个物理像素。而在移动端,大多数机型都不是为1,其中iphone的dpr普遍是2和3,那么一个css像素不再是对应一个物理像素,而是2个和3个物理像素。即我们通常在css中设置的width:1px,对应的便是物理像素中的2px。手机机型不同,dpr可能不同。
以iphone5为例,iphone5的CSS像素为320px568px,DPR是2,所以其设备像素为640px1136px
640(px) / 320(px) = 2
1136(px) / 568(px) = 2
640(px)*1136(px) / 320(px)*568(px) = 4
1
2
3
5、视口viewport
浏览器上(或者app中的webview)用来显示网页的那部分区域。业内总结出三个子概念。
布局视口 layout viewport:
手机一般为了容纳为桌面浏览器设计的网站,默认布局viewport宽度远大于屏幕的宽度,为了让用户看到网站全貌,缩小网站。例如,apple一般将viewport宽度定为980px。主要意义是手机厂商不至于让自家手机变得可笑,在打开大于980宽度的页面的时候可以横向拖动,而不至于挤成一团。可以通过document.documentElement.clientWidth来获取。
视觉视口 visual viewport:
屏幕的可视区域,即物理像素尺寸,可以通过window.innerWidth来获取。对于iPhone 6 Plus来说,在加了著名代码前提下,值是414px,不加的话,值是980px,著名代码如果改一改width=device-width, initial-scale=1.5,这时值是276px。所以它是一个可变的值。
理想视口 ideal viewport:
最适合移动设备的viewport,ideal viewport的宽度等于移动设备的屏幕宽度
为了让viewport能够等于ideal viewport,一般我们会添加meta标签。width=device-width,initial-scale=1.0的时候,视觉视口的大小。对于iPhone 6 Plus来说,是固定值414px。所以,理想视口就等于设备宽度。
1 2 3 4 二、移动端1px解决方案1、小数值px
解决思路:
1 2 3 4 5 6 7 8 9 优点: 简单,好理解
缺点:
兼容性差,目前之余IOS8+才支持,在IOS7及其以下、安卓系统都是显示0px。
2、border-image
使用的背景图片
代码:
.border-image-1px {
border-width: 1px 0px;
-webkit-border-image: url(border.png) 2 0 stretch;
border-image: url(border.png) 2 0 stretch;
}
1
2
3
4
5
6
优点:
图片可以用gif, png, base64多种格式, 以上是上下左右四条边框的写法, 需要单一边框只要定义单一边框的border, 代码比较直观.
缺点:
大小、颜色更改不灵活
放到PS里面看边框,是有点模糊的(因为带有颜色部分是1px,在retina屏幕上拉伸到2px肯定会有点模糊)
3、background-img渐变
设置1px的渐变背景,50%有颜色,50%透明
.border {
background:
linear-gradient(180deg, black, black 50%, transparent 50%) top left / 100% 1px no-repeat,
linear-gradient(90deg, black, black 50%, transparent 50%) top right / 1px 100% no-repeat,
linear-gradient(0, black, black 50%, transparent 50%) bottom right / 100% 1px no-repeat,
linear-gradient(-90deg, black, black 50%, transparent 50%) bottom left / 1px 100% no-repeat;
}
1
2
3
4
5
6
7
4、CSS3 box-shadow
.shadow {
-webkit-box-shadow:0 1px 1px -1px rgba(255, 0, 0, 0.5);
box-shadow:0 1px 1px -1px rgba(255, 0, 0, 0.5);
}
1
2
3
4
模拟效果:没觉得这个方法好用,模拟的效果差强人意,颜色也不好配置,不推荐
5、viewport和rem结合
viewport结合rem解决像素比问题
比如在devicePixelRatio=2设置meta
1 在devicePixelRatio=3设置meta 1 手淘采用这这种方案使用Flexible实现手淘H5页面的终端适配6、:before:after和transform
原理是把原先元素的 border 去掉,然后利用 :before 或者 :after 重做 border ,并 transform 的 scale 缩小一半,原先的元素相对定位,新做的 border 绝对定位。
单条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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
四条border样式设置:
.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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
结合js来代码来判断是否是Retina屏
if(window.devicePixelRatio && devicePixelRatio >= 2){
document.querySelector(‘div’).className = ‘scale-1px’;
}
1
2
3
4
优点:
所有场景都能满足
支持圆角
缺点:
对于已经使用伪类的元素,可能需要多层嵌套
总结
1、0.5px,相信浏览器肯定是会慢慢支持的;目前而言,如果能用的话,可以hack一下;
2、阴影,border-img的方案不建议使用
3、背景图片和缩放可以在项目中配合使用,如单个线条使用缩放,四条框用背景图片模拟,额,如果要圆角的话,无能无力了
4、建议采用transform和伪类
贴上自己的纠正代码
@charset "utf-8";
.border,
.border-top,
.border-right,
.border-bottom,
.border-left,
.border-topbottom,
.border-rightleft,
.border-topleft,
.border-rightbottom,
.border-topright,
.border-bottomleft {
position: relative;
}
.border::before,
.border-top::before,
.border-right::before,
.border-bottom::before,
.border-left::before,
.border-topbottom::before,
.border-topbottom::after,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::before,
.border-topleft::after,
.border-rightbottom::before,
.border-rightbottom::after,
.border-topright::before,
.border-topright::after,
.border-bottomleft::before,
.border-bottomleft::after {
content: "\0020";
overflow: hidden;
position: absolute;
}
/* border
* 因,边框是由伪元素区域遮盖在父级
* 故,子级若有交互,需要对子级设置
* 定位 及 z轴
*/
.border::before {
box-sizing: border-box;
top: 0;
left: 0;
height: 100%;
width: 100%;
border: 1px solid #eaeaea;
transform-origin: 0 0;
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
left: 0;
width: 100%;
height: 1px;
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
top: 0;
width: 1px;
height: 100%;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
border-top: 1px solid #eaeaea;
transform-origin: 0 0;
}
.border-right::before,
.border-rightbottom::before,
.border-rightleft::before,
.border-topright::after {
border-right: 1px solid #eaeaea;
transform-origin: 100% 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::before {
border-bottom: 1px solid #eaeaea;
transform-origin: 0 100%;
}
.border-left::before,
.border-topleft::after,
.border-rightleft::after,
.border-bottomleft::after {
border-left: 1px solid #eaeaea;
transform-origin: 0 0;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
top: 0;
}
.border-right::before,
.border-rightleft::after,
.border-rightbottom::before,
.border-topright::after {
right: 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::after {
bottom: 0;
}
.border-left::before,
.border-rightleft::before,
.border-topleft::after,
.border-bottomleft::before {
left: 0;
}
@media (max--moz-device-pixel-ratio: 1.49), (-webkit-max-device-pixel-ratio: 1.49), (max-device-pixel-ratio: 1.49), (max-resolution: 143dpi), (max-resolution: 1.49dppx) {
/* 默认值,无需重置 */
}
@media (min--moz-device-pixel-ratio: 1.5) and (max--moz-device-pixel-ratio: 2.49), (-webkit-min-device-pixel-ratio: 1.5) and (-webkit-max-device-pixel-ratio: 2.49), (min-device-pixel-ratio: 1.5) and (max-device-pixel-ratio: 2.49), (min-resolution: 144dpi) and (max-resolution: 239dpi), (min-resolution: 1.5dppx) and (max-resolution: 2.49dppx) {
.border::before {
width: 200%;
height: 200%;
transform: scale(.5);
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
transform: scaleY(.5);
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
transform: scaleX(.5);
}
}
@media (min--moz-device-pixel-ratio: 2.5), (-webkit-min-device-pixel-ratio: 2.5), (min-device-pixel-ratio: 2.5), (min-resolution: 240dpi), (min-resolution: 2.5dppx) {
.border::before {
width: 300%;
height: 300%;
transform: scale(.33333);
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
transform: scaleY(.33333);
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
transform: scaleX(.33333);
}
}
参考文献
像素概念
https://www.jianshu.com/p/73c3889ffec6
http://efe.baidu.com/blog/1px-on-retina/
http://www.fly63.com/article/detial/145
https://github.com/maxzhang/maxzhang.github.com/issues/4
http://www.cnblogs.com/2050/p/3877280.html
https://link.jianshu.com/?t=https://github.com/amfe/article/issues/17