一、问题现象
在移动端web开发中,UI设计稿中设置边框为1像素,前端在开发过程中如果出现border:1px,测试会发现在高清屏机型中,1px会比较粗,即是较经典的移动端1px像素问题。
二、产生原因
高清屏(retina屏)是指高dpr的设备,其物理像素的密度更大。又分为有两倍屏,三倍屏。
dpr:物理像素/css像素(逻辑像素)
在普通屏,1个css像素对应1个物理像素;2倍屏中,一个css像素对应4个物理像素;三倍屏中则是9个。
按照这样的置换规则后一张相同的图片在不同的设备上才会显示相同的大小。
两倍屏下用两排像素去展示1px的线,自然会比普通屏中用一排像素去展示看起来更粗。
三、两倍屏解决方案
1、0.5px
在IOS8+
,苹果系列都已经支持0.5px
了,可以借助媒体查询来处理。
/*这是css方式*/ .border { border: 1px solid #999 } @media screen and (-webkit-min-device-pixel-ratio: 2) { .border { border: 0.5px solid #999 } } /*ios dpr=2和dpr=3情况下border相差无几,下面代码可以省略*/ @media screen and (-webkit-min-device-pixel-ratio: 3) { .border { border: 0.333333px solid #999 } }
IOS7
及以下和Android
等其他系统里,0.5px
将会被显示为0px
。那么我们就需要想出办法解决。
解决方案是通过JavaScript
检测浏览器能否处理0.5px
的边框,如果可以,给html
标签元素添加个class
。
if (window.devicePixelRatio && devicePixelRatio >= 2) { var testElem = document.createElement('div'); testElem.style.border = '.5px solid transparent'; document.body.appendChild(testElem); } if (testElem.offsetHeight == 1) { document.querySelector('html').classList.add('hairlines'); } document.body.removeChild(testElem); } // 脚本应该放在body内,如果在里面运行,需要包装 $(document).ready(function() {})
然后,极细的边框样式就容易了:
div { border: 1px solid #bbb; } .hairlines div { border-width: 0.5px; }
优点:简单,不需要过多代码。
缺点:无法兼容安卓设备、 iOS 7
及以下设备。
2、伪类+transform
伪元素::after
或::before
是独立于当前元素,可以单独对其缩放而不影响元素本身的缩放。
<div class="border-1px">cell</div> .border-1px { width: 100px; height: 100px; } /*全部边框*/ .border-1px:after { content: ''; position: absolute; box-sizing: border-box; top: 0; left: 0; width: 200%; height: 200%; border: 1px solid #000; border-radius: 4px; -webkit-transform: scale(0.5); transform: scale(0.5); -webkit-transform-origin: top left; }
优点:所有场景都能满足,支持圆角(伪类和本体类都需要加border-radius)。
缺点:代码量也很大,对于已经使用伪类的元素(例如clearfix),可能需要多层嵌套。
3、viewport + rem
同时通过设置对应viewport
的rem
基准值,这种方式就可以像以前一样轻松愉快的写1px了。
在devicePixelRatio = 2
时,设置meta
:
<meta name="viewport" content="width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
在devicePixelRatio = 3
时,设置meta
:
<meta name="viewport" content="width=device-width,initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no">
优点:所有场景都能满足,一套代码,可以兼容基本所有布局。
缺点:老项目修改代价过大,只适用于新项目。
4、border-image以及background-image
将图片设置为边框
优点:可以设置单条,多条边框,没有性能瓶颈的问题;
缺点:修改颜色麻烦, 需要替换图片;圆角需要特殊处理,并且边缘会模糊。
使用border-image
每次都要去调整图片,可以借助于PostCSS
的插件postcss-write-svg
来帮助我们。
5、box-shadow
可以使用box-shadow来模拟边框
.border-1px { width: 100px; height: 100px; box-shadow: inset 0px -1px 1px -1px #c8c7cc; }
优点:代码量少,可以满足所有场景
缺点:边框有阴影,颜色变浅
补充:
box-shadow: none | inset(可选值,不设置,为外投影,设置,为内投影) x-offset(阴影水平偏移量,正方向为right) y-offset(阴影垂直偏移量,正方向为bottom) blur-radius(阴影模糊半径,为正,0为无模糊效果,值越大,越模糊) spread-radius(阴影扩展半径,可正可负) color
属性值描述:
1.阴影类型:此参数可选,默认的投影方式是外阴影;如果取其唯一值“inset”,就是将外阴影变成内阴影
2.X-offset:是指阴影水平偏移量,其值可正可负,正值,则阴影在对象的右边,负值,阴影在对象的左边
3. Y-offset:是指阴影的垂直偏移量,其值也可以是正负值,正值,阴影在对象的底部,负值时,阴影在对象的顶部
4.阴影模糊半径:此参数是可选,只能为正值,如果其值为0时,表示阴影不具有模糊效果,值越大阴影的边缘就越模糊
5. 阴影扩展半径:此参数可选,其值可为正负值,正值,则整个阴影都延展扩大,反之,则缩小
6.阴影颜色:此参数可选,不设定任何颜色时,浏览器会取默认色,但各浏览器默认色不一样,特别是在webkit内核下的safari和chrome浏览器将无色,也就是透明,建议不要省略此参数。