1. 程式人生 > >移動端1px畫素問題及解決辦法

移動端1px畫素問題及解決辦法

        在移動端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;

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畫素為320px*568px,DPR是2,所以其裝置畫素為640px*1136px

640(px) / 320(px)  = 2
1136(px) / 568(px) = 2
640(px)*1136(px) /  320(px)*568(px) = 4

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。所以,理想視口就等於裝置寬度。

<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/>
<!--width=device-width:寬度為裝置寬度-->
<!--initial-scale:縮放比為1-->
<!--user-scalable=no:是否允許使用者自定義縮放-->

二、移動端1px解決方案

1、小數值px

解決思路:

<body>
	<div id="main" style="border: 1px solid #000000;"></div>
</body>
<script type="text/javascript">
if (window.devicePixelRatio && devicePixelRatio >= 2) {
	var main = document.getElementById('main');
	main.style.border = '.5px solid #000000';
}
</script>

優點:

        簡單,好理解

缺點:

        相容性差,目前之餘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;
}

優點:

        圖片可以用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;
}

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);
}

模擬效果:沒覺得這個方法好用,模擬的效果差強人意,顏色也不好配置,不推薦

5、viewport和rem結合

viewport結合rem解決畫素比問題

比如在devicePixelRatio=2設定meta

<meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">

devicePixelRatio=3設定meta

<meta name="viewport" content="initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no">

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; 
}

四條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; 
}
結合js來程式碼來判斷是否是Retina屏
if(window.devicePixelRatio && devicePixelRatio >= 2){
	 document.querySelector('div').className = 'scale-1px';
}

優點:

        所有場景都能滿足

        支援圓角

缺點:

        對於已經使用偽類的元素,可能需要多層巢狀

總結

1、0.5px,相信瀏覽器肯定是會慢慢支援的;目前而言,如果能用的話,可以hack一下;

2、陰影,border-img的方案不建議使用

3、背景圖片和縮放可以在專案中配合使用,如單個線條使用縮放,四條框用背景圖片模擬,額,如果要圓角的話,無能無力了

4、建議採用transform和偽類

參考文獻

畫素概念

http://efe.baidu.com/blog/1px-on-retina/

https://link.jianshu.com/?t=https://github.com/amfe/article/issues/17