1px? 0.5px!
前一段時間剛開發完一個項目,但總感覺界面樣式怪怪的,雖然表面上看起來和設計稿是一樣的,可是就是沒設計稿那種感覺,而且莫名還有一種山寨的氣息,讓我感到很郁悶,找來找去終於發現罪魁禍首——border線寬的問題。
問題產生的原因
關於設備像素的基礎知識,建議去看 《A pixer is not a pixer is not a pixer》
簡單來說就是手機屏幕分辨率越來越高了,同樣大小的一個手機,它的實際物理像素數更多了。現在做移動端開發時一般都要加上一個<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">這種,來禁止縮放,這樣在不同手機上顯示就一樣了。這種情況下我們做一個1px的border時,這個1px實際就是css像素,在2倍分辨率的手機上那就是占用了2個設備像素(device pixer),在3倍尺寸就是占3個設備像素。但是呢,設計師在PS或sketch中進行設計時設計的像素肯定是按設備像素來的(*其實就跟字體一個原理,設計稿字體大小是28px,但實際代碼卻是14px*),所以嚴格來說設計師希望你呈現的那個邊框寬度,就是0.5px的css像素。
解決辦法
1. 利用css3新屬性scale
其實現的關鍵就是利用before或after偽元素,寬高同時設為200%,然后再縮放0.5,自然就是0.5px了
代碼如下:
.scale_border { positon : relative; } .scale_border::after { content : ""; width : 200%; height : 200%; position : absolute; top : 0; left : 0; border : 1px solid #dedede; -webkit-transform : scale(0.5); transform : scale(0.5); -webkit-transform-origin : 0 0; transform-origin : 0 0; box-sizing: border-box; }
2. 利用background-image
如果你問切圖直接用背景圖可不可以?當然可以,雖說這種方法比較笨吧,但也是一種方法。
但我要說的是另外一種--利用背景漸變linear-gradient來實現,具體代碼如下:
.bg_border { background-image : linear-gradient(0deg,red,red 50%,transparent 50%); background-size: 100% 1px; background-repeat: no-repeat; }
分析:linear-gradient默認方向從上到下,從0deg到50%的地方顏色是邊框顏色,然后下邊一半顏色就是透明了-沒顏色。中間之所以兩個50%寫在一起,是因為這樣就不會有顏色過渡的漸變效果了,看起來更像一條線,涇渭分明; 然后最關鍵的是下邊的background-size: 100% 1px,就是寬度100%,但高度是1px,注意這里的1px自然是css像素了,加上上邊的background-image,實際效果就是一半有顏色,一半那不就是0.5px,然后再去掉repeat,就實現了。
同理如果要寫border-left或border-right一樣的原理,只需改變方向就可以了。
缺點: 只能做但方向的border,如果有個按鈕要加,而且還有圓角,那就無能為力了
3.利用rem做單位
首先對rem還不太了解的同學,請點這里《web app變革之rem》
簡單來講,為了完美自適應,rem是一個很好的解決方案,原理就是在剛加載的時候用js判斷屏幕分辨率,根據不同的分辨率動態為html設置不同font-size大小,設置meta的viewport標簽。
如果你覺得麻煩的話,那可以簡單一些,直接引用淘寶的flexible.js,看其源代碼的話會發現其實很簡短,其核心代碼就更少了,在這里我僅提取其部分代碼來做分析,如果感興趣請自行百度。
var doc = windows.document; var docEl = doc.documentElement; var metaEl = doc.createElement('meta'); var devicePixelRatio = window.devicePixelRatio; var dpr = 0, scale = 0; if(devicePixelRatio == 1){ dpr = 1; }else if (devicePixelRatio == 2){ dpr = 2; }else if (devicePixelRatio == 3){ dpr = 3; } scale = 1/dpr; metaEl.setAttribute('name', 'viewport'); metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no'); docEl.firstElementChild.appendChild(metaEl); function refreshRem(){ var width = docEl.getBoundingClientRect().width; if (width / dpr > 540) { width = 540 * dpr; } var rem = width / 10; docEl.style.fontSize = rem + 'px'; flexible.rem = win.rem = rem; }; refreshRem();
解釋一下,就是先獲取設備縮放比devicePixelRatio;然后根據縮放比來設定viewport的值,這樣導致的結果就是無論是哪個設備,1px所表示的永遠是1個設備像素,即該設備的最小像素,用法就更簡單了
//直接使用px就能實現 .rem_border { border : 1px solid #dedede; }
優點: 目前最優解決方案,兼容性強,即使有導圓角也能輕松應付
缺點: 對舊項目改動較大,不易改動
持續測試中~