一、移动端1px变粗的原因
为什么移动端css里面写了1px, 实际看起来比1px粗,其实原因很好理解:这个px的含义是不一样的。移动端html的header总会有一句
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
这句话定义了本页面的viewport的宽度为设备宽度,初始缩放值和最大缩放值都为1,并禁止了用户缩放。viewport通俗的讲是浏览器上可用来显示页面的区域,这个区域是可能比屏幕大的。根据这篇文章http://www.cnblogs.com/2050/p/3877280.html的分析, 手机存在一个能完美适配的理想viewport,分辨率相差很大的手机的理想viewport的宽度可能是一样的,这样做的目的是为了保证同样的css在不同屏幕下的显示效果是一致的,上面的meta实际上是设置了ideal viewport的宽度。
以实际举例:iphone3和iphone4的屏幕宽度分别是320px,640px,但是它们的ideal viewport的宽度都是320px,设置了设备宽度后,320px宽的元素都能100%的填充满屏幕宽。不同手机的ideal viewport宽度是不一样的,常见的有320px, 360px, 384px。iphone系列的这个值在6之前都是320px,控制viewport的好处就在于一套css可以适配多个机型。
看懂的人应该已经明白 1px 变粗的原因了,viewport的设置和屏幕物理分辨率是按比例而不是相同的,移动端 window 对象有个devicePixelRatio属性,它表示设备物理像素和css像素的比例,在retina屏的iphone手机上,这个值为2或3,css里写的 1px 长度映射到物理像素上就有 2px 或 3px 那么长。
简言之,就是说1px变粗是由于不同的手机有不同的像素密度导致的。如果移动显示屏的分辨率始终是普通屏幕的2倍,1px的边框在devicePixelRatio=2的移动显示屏下会显示成2px,所以在高清屏下看着1px总是感觉变胖了。
产生原因
1、设备像素比:dpr=window.devicePixelRatio,也就是设备的物理像素与逻辑像素的比值。
2、在retina
屏的手机上, dpr
为2
或3
,css
里写的1px
宽度映射到物理像素上就有2px
或3px
宽度。例如:iPhone6
的dpr
为2
,物理像素是750
(x轴),它的逻辑像素为375
。也就是说,1个逻辑像素,在x
轴和y
轴方向,需要2个物理像素来显示,即:dpr=2时,表示1个CSS像素由4个物理像素点组成。
二、解决方案
1、用小数来写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 } }
如果使用less/sass的话只是加了1句mixin
缺点: 安卓与低版本IOS不适用,这个或许是未来的标准写法,现在不做指望
2、伪元素 + transform 实现
对于老项目 伪元素 + transform 是比较完美的方法了。
原理是把原先元素的 border 去掉,然后利用 :before 或者 :after 重做 border ,并 transform 的 scale 缩小一半,原先的元素相对定位,新做的 border 绝对定位。
构建1个伪元素,将它的长宽放大到2倍,边框宽度设置为1px,再以transform缩放到50%。
.radius-border{ position: relative; } @media screen and (-webkit-min-device-pixel-ratio: 2){ .radius-border:before{ content: ""; pointer-events: none; /* 防止点击触发 */ box-sizing: border-box; position: absolute; width: 200%; height: 200%; left: 0; top: 0; border-radius: 8px; border:1px solid #999; -webkit-transform(scale(0.5)); -webkit-transform-origin: 0 0; transform(scale(0.5)); transform-origin: 0 0; } }
需要注意<input type="button">是没有:before, :after伪元素的
优点: 其实不止是圆角, 其他的边框也可以这样做出来
缺点: 代码量也很大, 占据了伪元素, 容易引起冲突
3,viewport + rem 实现
这种兼容方案相对比较完美,适合新的项目,老的项目修改成本过大。
在devicePixelRatio = 2 时,输出viewport:
在devicePixelRatio = 3 时,输出viewport:
优点:所有场景都能满足,一套代码,可以兼容基本所有布局
缺点:老项目修改代价过大,只适用于新项目
4,使用box-shadow模拟边框
利用css 对阴影处理的方式实现0.5px的效果。样式设置:
.box-shadow-1px { box-shadow: inset 0px -1px 1px -1px #c8c7cc; }
优点:代码量少,可以满足所有场景
缺点:边框有阴影,颜色变浅