前言
移動端web專案越來越多,設計師對於UI的要求也越來越高,比如1px 的邊框。在高清屏
下,移動端的1px 會很粗。
比如,這個是假的1畫素
這個是真的1畫素
一、產生原因
那麼為什麼會產生這個問題呢?主要是跟一個東西有關,DPR(devicePixelRatio) 裝置畫素比,它是預設縮放為100%的情況下,裝置畫素和CSS畫素的比值。
window.devicePixelRatio=物理畫素 /CSS畫素
複製程式碼
目前主流的螢幕DPR=2 (iPhone 8),或者3 (iPhone 8 Plus)。拿2倍屏來說,裝置的物理畫素要實現1畫素,而DPR=2,所以css 畫素只能是 0.5。一般設計稿是按照750來設計的,它上面的1px是以750來參照的,而我們寫css樣式是以裝置375為參照的,所以我們應該寫的0.5px就好了啊! 試過了就知道,iOS 8+系統支援,安卓系統不支援。
二、解決方案
1、WWDC對iOS統給出的方案
推薦指數:**
在 WWDC大會上,給出來了1px方案,當寫 0.5px的時候,就會顯示一個物理畫素寬度的 border,而不是一個css畫素的 border。 所以在iOS下,你可以這樣寫。
border:0.5px solid #E5E5E5
複製程式碼
可能你會問為什麼在3倍屏下,不是0.3333px 這樣的?經過我測試,在Chrome上模擬iPhone 8Plus,發現0.46-顯示不出來。
總結:
- 優點:簡單,沒有副作用
- 缺點:支援iOS 8+,不支援安卓。後期安卓follow就好了。
2、使用邊框圖片
推薦指數:**
border: 1px solid transparent;
border-image: url('./../../image/96.jpg') 2 repeat;
複製程式碼
圖片自己隨便截圖的,建議自己做一張圖片。
圖片的顏色就是此後border的顏色
這個方法在W3CPlus 上的例子講的非常細緻 www.w3cplus.com/content/css… 。
總結:
- 優點:沒有副作用
- 缺點:border顏色變了就得重新制作圖片;圓角會比較模糊。
3、使用box-shadow實現
推薦指數:***
先複習一下box-shadow,看一下MDN 上的這篇就夠了 developer.mozilla.org/zh-CN/docs/… 。
再看一下效果
程式碼是怎樣實現的呢?
box-shadow: 0 -1px 1px -1px #e5e5e5, //上邊線
1px 0 1px -1px #e5e5e5, //右邊線
0 1px 1px -1px #e5e5e5, //下邊線
-1px 0 1px -1px #e5e5e5; //左邊線
複製程式碼
前面兩個值 x,y 主要控制顯示哪條邊,後面兩值控制的是陰影半徑、擴充套件半徑。 其實方法可以到這個地址線上嘗試一下 。
總結
- 優點:使用簡單,圓角也可以實現
- 缺點:模擬的實現方法,仔細看誰看不出來這是陰影不是邊框。
4、使用偽元素
推薦指數:****
這個方法是我使用最多的,做出來的效果也是非常棒的,直接上程式碼。
1 條border
.setOnePx{
position: relative;
&::after{
position: absolute;
content: '';
background-color: #e5e5e5;
display: block;
width: 100%;
height: 1px; /*no*/
transform: scale(1, 0.5);
top: 0;
left: 0;
}
}
複製程式碼
可以看到,將偽元素設定相對定位,並且和父元素的左上角對其,將width 設定100%,height設定為1px,然後進行在Y方向縮小0.5倍
。
4 條border
.setBorderAll{
position: relative;
&:after{
content:" ";
position:absolute;
top: 0;
left: 0;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: left top;
box-sizing: border-box;
border: 1px solid #E5E5E5;
border-radius: 4px;
}
}
複製程式碼
同樣為偽元素設定相對定位,並且和父元素左上角對其。將偽元素的長和寬先放大2倍,然後再設定一個邊框,以左上角為中心,縮放到原來的0.5倍
總結:
- 優點:全機型相容,實現了真正的1px,而且可以圓角。
- 缺點:暫用了after 偽元素,可能影響清除浮動。
5、設定viewport的scale值
推薦指數:*****
這個解決方案是利用viewport+rem+js 實現的。 參考了網上的一個例子 移動端1畫素邊框問題的解決方案,自己手動實現了一下。
效果不錯。 上程式碼<html>
<head>
<title>1px question</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<meta name="viewport" id="WebViewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<style>
html {
font-size: 1px;
}
* {
padding: 0;
margin: 0;
}
.top_b {
border-bottom: 1px solid #E5E5E5;
}
.a,.b {
box-sizing: border-box;
margin-top: 1rem;
padding: 1rem;
font-size: 1.4rem;
}
.a {
width: 100%;
}
.b {
background: #f5f5f5;
width: 100%;
}
</style>
<script>
var viewport = document.querySelector("meta[name=viewport]");
//下面是根據裝置畫素設定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.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no');
}
var docEl = document.documentElement;
var fontsize = 32* (docEl.clientWidth / 750) + 'px';
docEl.style.fontSize = fontsize;
</script>
</head>
<body>
<div class="top_b a">下面的底邊寬度是虛擬1畫素的</div>
<div class="b">上面的邊框寬度是虛擬1畫素的</div>
</body>
</html>
複製程式碼
總結
- 優點:全機型相容,直接寫
1px
不能再方便 - 缺點:適用於新的專案,老專案可能改動大
三、踩過坑
一部血淚史。。。。征服不了UI,只能征服自己。
1、使用偽元素
方法,偽類裡面再設定偽元素,可以選擇到嗎?
看圖,需要改中間的豎線
上程式碼 &:nth-child(2){
//border-color: #e5e5e5 !important;
border: 0;
position: relative;
&:after{
position: absolute;
content: '';
background-color: #e5e5e5;
display: block;
width: 100%;
height: 1px; /*no*/
transform: scale(1, 0.5);
top: 0;
}
}
複製程式碼
然而上面程式碼展示出來的樣式是這樣的
為什麼中間的豎線沒有了?!最初我以為在偽類下面,再寫偽元素after,可能會拿不到。 看到這裡發現,是有after偽元素的,但是好像位置不對,跑到上面去了。我是想要豎線的,到底什麼原因呢?最後在安哥的找了一種方法解決了這個問題,才明白其中的真相。原來是我的width 和 height 設定的有問題,對於豎線,應該是width =1px,height=100%,然後再縮放 X 方向
0.5倍
,這樣豎線就出來了;同樣,設定水平線,應該是width=100%,height=1px,然後再縮放Y方向0.5倍
。
知道了原因,再也不擔心寫錯了。
&:nth-child(2){
position: relative;
&:after{
position: absolute;
content: '';
top: 0;
left: 0;
width: 1px;
height: 100%;
transform: scaleX(0.5);
background: #e5e5e5;
transform-origin: 0 0;
}
}
複製程式碼
這樣是可以的,
&:nth-child(2){
position: relative;
&:after{
content:" ";
position:absolute;
top: 0;
left: 0;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: left top;
box-sizing: border-box;
border-left: 1px solid #E5E5E5;
}
}
複製程式碼
這樣也可以的。
2、為什麼還是拿不到偽元素
?
我以為知道了上面的方法就可以快快樂樂的寫1畫素 border 了,然而馬上又懷疑自己了。
上面這個輸入框的1畫素,需要的所有的border都是1px,我使用了寬高放大200%後再縮小0.5倍
的方法。
這就奇怪了,同樣的方法在別的地方都有效的,為什麼在這裡不顯示了。找了好久,最後找到這篇文章使用 CSS 偽元素需要注意的 ,然後發現:
所以不顯示的原因找到了,輸入框Textarea不支援偽元素
沒辦法了,偽元素的方法不能用,只能使用其他的方法了。
四、總結
總結一下,新專案最好使用的是設定viewport的scale值
,這個方法相容性好,後期寫起來方便。老專案的話,改起來可能比較多,用的比較多的方法就是偽元素+transform
的方法。
其他的背景圖片,陰影的方法畢竟還是不太靈活,而且相容性不好。