移動端1像素邊框問題

? 對于前端開發(fā)者來說,要處理這個問題,必須先補充一個知識點,就是設(shè)備的 物理像素[設(shè)備像素] & 邏輯像素[CSS像素];在我的另外一片文章 設(shè)計稿的100%還原 做了介紹,感興趣的朋友可以查閱一下。

# 為什么1px變粗了?

? 為什么移動端CSS里面寫了1px,實際上看起來比1px粗;了解設(shè)備物理像素和邏輯像素的同學(xué)應(yīng)該很容易理解,其實這兩個px的含義其實是不一樣的,UI設(shè)計師要求的1px是指設(shè)備的物理像素1px,而CSS里記錄的像素是邏輯像素,它們之間存在一個比例關(guān)系,可以用javascript中的window.devicePixelRatio來獲取,也可以用媒體查詢的-webkit-min-device-pixel-ratio來獲取。當(dāng)然,比例多少與設(shè)備相關(guān)。
在手機上border無法達到我們想要的效果。這是因為devicePixelRatio特性導(dǎo)致,iPhone的devicePixelRatio==2,而border-width: 1px描述的是設(shè)備獨立像素,所以,border被放大到物理像素2px顯示,在iPhone上就顯得較粗。

? 移動端開發(fā)常需要在html的header里添加如下一句:
? <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
? 這句話定義了本頁面的viewport的寬度為設(shè)備寬度,初始縮放值和最大縮放值都為1,并禁止了用戶縮放。

? 你的疑問可能來了,老是看到viewport,除了知道中文名叫視口,到底是啥意思?其實,它就是設(shè)備屏幕上能用來顯示我們網(wǎng)頁內(nèi)容的那一塊區(qū)域,具體點就是瀏覽器或app中的webview用來顯示網(wǎng)頁的那部分區(qū)域,但viewport又不局限于瀏覽器可視區(qū)域的大小,可能大也可能小。體現(xiàn)在用戶是否縮放了屏幕。

? 心細的同學(xué)應(yīng)該都有感覺,meta標(biāo)簽中常設(shè)置user-scalable=no也就是禁止用戶縮放,那用戶縮放到底會造成什么影響呢? 其實也就是顯示上的變化??s放一倍,CSS像素(邏輯像素)所代表的物理像素也就縮放了一倍,即設(shè)備物理像素和設(shè)備獨立像素的比例增大(減小)了一倍。

? 或許你已經(jīng)明白1px變粗的原因是啥了, viewport的設(shè)置和屏幕物理分辨率是按比例而不是相同的. 移動端window對象有個devicePixelRatio屬性, 它表示設(shè)備物理像素和css像素的比例, 在retina屏的iphone手機上, 這個值為2或3, css里寫的1px長度映射到物理像素上就有2px或3px那么長。
?

# 如何解決1px問題

1. 媒體查詢利用設(shè)備像素比縮放,設(shè)置小數(shù)像素

? IOS8下已經(jīng)支持帶小數(shù)的px值, media query對應(yīng)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 }
}

缺點】對設(shè)備有要求,小數(shù)像素目前兼容性較差。

2. viewport + rem 方案

? 該方案是對上述方案的優(yōu)化,整體思路就是利用viewport + rem + js 動態(tài)的修改頁面的縮放比例,實現(xiàn)小于1像素的顯示。在頁面初始化時,在頭部引入原始默認狀態(tài)如下:

<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"> 

接下來的任務(wù)就是js的動態(tài)修改縮放比 以及 實現(xiàn)rem根元素字體大小的設(shè)置。

var viewport = document.querySelector("meta[name=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.333333333, maximum-scale=0.333333333, minimum-scale=0.333333333, user-scalable=no')
} 

var docEl = document.documentElement;
var fontsize = 10 * (docEl.clientWidth / 320) + 'px';
docEl.style.fontSize = fontsize;

缺點】以為縮放涉及全局的rem單位,比較適合新項目,對于老項目可能要涉及到比較多的改動。

3. 設(shè)置 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;
}

解釋border-width 指定邊框的寬度,可以設(shè)定四個值,分別為上右下左border-width: top right bottom left。
border-image 該例意為:距離圖片上方2px(屬性值上沒有單位)裁剪邊框圖片作為上邊框,下方2px裁剪作為下邊框。距離左右0像素裁剪圖片即沒有邊框,以拉伸方式展示

結(jié)合起來就是:在邊框圖片中,裁剪圖片上下方的2個像素寬度作為上下邊框,并展示在寬度為1個像素的邊框空間里。左右沒有邊框。 注意這里的1個像素是特殊的,專指物理像素,而平時設(shè)定的長寬1px則表示邏輯像素(該觀點為個人理解)。

? 你可能并沒有理解,裁剪邊框是什么鬼?為啥還要裁剪?border-image確實是個很難理解的屬性,是去w3c看了之后更加蒙圈了。w3c內(nèi)容提煉的太精致了,但解釋根本就不夠。因此奉上張鑫旭老師 border-image詳解 一文,特別要理解九宮格裁剪法。

當(dāng)然,這種方式引入了圖片,我們還能將圖片裝換成base64形式表現(xiàn)

.border-image-1px {
    border-width: 1px 0px;
    -webkit-border-image: url("data:image/png;base64,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") 2 0 stretch;
    border-image: url('如上');
}

缺點】需要制作圖片,圓角可能出現(xiàn)模糊

4. background-image 漸變實現(xiàn)

? 除了使用圖片外,當(dāng)然也能使用純css來實現(xiàn),百度糯米團就是采用的這種方案。

.border {
      background-image:linear-gradient(180deg, red, red 50%, transparent 50%),
      linear-gradient(270deg, red, red 50%, transparent 50%),
      linear-gradient(0deg, red, red 50%, transparent 50%),
      linear-gradient(90deg, red, red 50%, transparent 50%);
      background-size: 100% 1px,1px 100% ,100% 1px, 1px 100%;
      background-repeat: no-repeat;
      background-position: top, right top,  bottom, left top;
      padding: 10px;
  }

思路】將原本1個物理像素的邊框大小利用線性漸變分割成幾個部分(百分比控制),實現(xiàn)小于1像素效果
解釋linear-gradient指定線性漸變,接受大于等于3個參數(shù),第一個為漸變旋轉(zhuǎn)角度,第二個開始為漸變的顏色和到哪個位置(百分比)全部變?yōu)樵擃伾?,該例子中,第一句就是?strong>漸變方向旋轉(zhuǎn)180度,即從上往下(默認為0度從下往上),從紅色開始漸變,到50%的位置還是紅色,再漸變?yōu)槔^承父元素顏色。
缺點】因為每個邊框都是線性漸變顏色實現(xiàn),因此無法實現(xiàn)圓角
?

5. box-shadow 方案

利用陰影也可以實現(xiàn),優(yōu)點是沒有圓角問題,缺點是顏色不好控制

div {
    -webkit-box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.5);
}

理解】來回顧一下box-shadow屬性的用法。

box-shadow: h-shadow v-shadow [blur] [spread] [color] [inset];

參數(shù)分別表示: 水平陰影位置,垂直陰影位置,模糊距離, 陰影尺寸,陰影顏色,將外部陰影改為內(nèi)部陰影,后四個可選。該例中為何將陰影尺寸設(shè)置為負數(shù)?設(shè)置成-1px 是為了讓陰影尺寸稍小于div元素尺寸,這樣左右兩邊的陰影就不會暴露出來,實現(xiàn)只有底部一邊有陰影的效果。從而實現(xiàn)分割線效果(單邊邊框)。
?

6. transform: scale(0.5) 方案 - 推薦: 很靈活

? 在以上的用法種,無非逃不開一種思想,就是將1px縮小為0.5px來展示,然而。0.5px并不是所有的設(shè)備或瀏覽器都支持,就考慮用媒體查詢或viewport將其縮放比例。其實1像素問題的產(chǎn)生基本發(fā)生在設(shè)置邊框或分割線的時候,場景并不覆蓋全局樣式,因此,直接縮放需要設(shè)置的元素,才是我們真正需要的。tranform就能實現(xiàn)這個需求。

  1. 設(shè)置height: 1px,根據(jù)媒體查詢結(jié)合transform縮放為相應(yīng)尺寸。
div {
    height:1px;
    background:#000;
    -webkit-transform: scaleY(0.5);
    -webkit-transform-origin:0 0;
    overflow: hidden;
}

2.用::after::befor,設(shè)置border-bottom:1px solid #000,然后在縮放-webkit-transform: scaleY(0.5);可以實現(xiàn)兩根邊線的需求

div::after{
    content:'';width:100%;
    border-bottom:1px solid #000;
    transform: scaleY(0.5);
}

3.用::after設(shè)置border:1px solid #000; width:200%; height:200%,然后再縮放scaleY(0.5); 優(yōu)點可以實現(xiàn)圓角,京東就是這么實現(xiàn)的,缺點是按鈕添加active比較麻煩。

.div::after {
    content: '';
    width: 200%;
    height: 200%;
    position: absolute;
    top: 0;
    left: 0;
    border: 1px solid #bfbfbf;
    border-radius: 4px;
    -webkit-transform: scale(0.5,0.5);
    transform: scale(0.5,0.5);
    -webkit-transform-origin: top left;
}
# 6.5 媒體查詢 + transfrom 對方案1的優(yōu)化
/* 2倍屏 */
@media only screen and (-webkit-min-device-pixel-ratio: 2.0) {
    .border-bottom::after {
        -webkit-transform: scaleY(0.5);
        transform: scaleY(0.5);
    }
}

/* 3倍屏 */
@media only screen and (-webkit-min-device-pixel-ratio: 3.0) {
    .border-bottom::after {
        -webkit-transform: scaleY(0.33);
        transform: scaleY(0.33);
    }
}

?

# 后語

? 解決方案主要提供一個思路,具體實現(xiàn)還需要自己手動嘗試。歡迎交流共進。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容