移動端1px解決方案

寧靜的冬日發表於2019-03-15

前言

移動端web專案越來越多,設計師對於UI的要求也越來越高,比如1px 的邊框。在高清屏下,移動端的1px 會很粗。

比如,這個是假的1畫素

移動端1px解決方案

這個是真的1畫素

移動端1px解決方案

一、產生原因

那麼為什麼會產生這個問題呢?主要是跟一個東西有關,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;
複製程式碼

圖片自己隨便截圖的,建議自己做一張圖片。

移動端1px解決方案

圖片的顏色就是此後border的顏色

這個方法在W3CPlus 上的例子講的非常細緻 www.w3cplus.com/content/css…

總結:

  • 優點:沒有副作用
  • 缺點:border顏色變了就得重新制作圖片;圓角會比較模糊。

3、使用box-shadow實現

推薦指數:***

先複習一下box-shadow,看一下MDN 上的這篇就夠了 developer.mozilla.org/zh-CN/docs/…

再看一下效果

移動端1px解決方案

程式碼是怎樣實現的呢?

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畫素邊框問題的解決方案,自己手動實現了一下。

移動端1px解決方案
效果不錯。 上程式碼

<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、使用偽元素方法,偽類裡面再設定偽元素,可以選擇到嗎?

看圖,需要改中間的豎線

移動端1px解決方案
上程式碼

      &: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;
        }
      }
複製程式碼

然而上面程式碼展示出來的樣式是這樣的

移動端1px解決方案
為什麼中間的豎線沒有了?!最初我以為在偽類下面,再寫偽元素after,可能會拿不到。

移動端1px解決方案
看到這裡發現,是有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 了,然而馬上又懷疑自己了。

移動端1px解決方案
上面這個輸入框的1畫素,需要的所有的border都是1px,我使用了寬高放大200%後再縮小0.5倍的方法。

移動端1px解決方案
這就奇怪了,同樣的方法在別的地方都有效的,為什麼在這裡不顯示了。找了好久,最後找到這篇文章使用 CSS 偽元素需要注意的 ,然後發現:

移動端1px解決方案
所以不顯示的原因找到了,輸入框Textarea不支援偽元素

沒辦法了,偽元素的方法不能用,只能使用其他的方法了。

四、總結

總結一下,新專案最好使用的是設定viewport的scale值,這個方法相容性好,後期寫起來方便。老專案的話,改起來可能比較多,用的比較多的方法就是偽元素+transform的方法。

其他的背景圖片,陰影的方法畢竟還是不太靈活,而且相容性不好。

相關文章