一、前言
有時候需要前端做到移動端高清顯示,1、面對開發移動端H5頁面,2、面對不同分辨率的手機,3、面對不同屏幕尺寸的手機。
二、視覺稿
前端開發正常是,視覺的小哥哥給我們psd或者導出的skech文件,這個就是視覺稿,然後前端開始寫結構,寫元素,調整,優化等等。
對於移動端開發而言,達到頁面高清的效果,視覺稿的規範往往會遵循這兩點:
(1)選取一款手機的屏幕寬高作爲基準(以前是iphone4的320*480,現在更多的是iphone6的375*667)。
(2)對於retina屏幕(可以將更多的像素點壓縮至一塊屏幕裏,從而達到更高的分辨率並提高屏幕顯示的細膩程度)(如:dpr=2),爲了達到高清效果,視覺稿的畫布大小會是基準的2倍,也就是說像素點個數是原來的4倍(比如iphone6的,原先的375*667,就會變成750*1334)。
肯定有疑問:(1)爲啥dpr=2的手機,爲什麼畫布大小*2,就可以解決高清問題?(2)對於2倍大小的視覺稿,具體的css是如何還原每一個區塊的真實寬高(佈局問題)?
三、基礎概念
1、物理像素:一個物理像素是顯示器(手機屏幕)上最小的物理顯示單元,在操作系統的調度下,每一個設備像素都有自己的顏色值和亮度值。
2、設備獨立像素:設備獨立像素也叫與密度無關像素,可以認爲是計算機座標系統中得到一個點,這個點代表一個可以由程序使用的虛擬像素(比如:css像素),然後由相關係統轉換爲物理像素。
所以,物理像素和設備獨立像素之間存在一定的對應關係,這就是要說的設備像素比。
3、設備像素比:設備像素比(簡稱爲dpr)定義了物理像素和設備獨立像素的對應關係,它的值可以根據公式計算得出:
設備像素比 = 物理像素 / 設備獨立像素 // 在某一方向上,x方向或者y方向
在javascript中,可以通過window.devicePixelRatio方法獲取當前設備的dpr。
在css中,通過-webkit-device-pixel-ratio,-webkit-min-device-pixel-ratio和-webkit-max-device-pixel-ratio進行媒體查詢,對於不同的dpr的設備,做一些樣式適配(主要是針對webkit內核的瀏覽器和webview)。
4、舉個例子
以iphone6爲例:
(1)設備寬高爲375*667,可以理解成設備的獨立像素(css像素);
(2)dpr爲2,根據上面計算公式,物理像素應該就是750*1334。
用一張圖來表現,就是這樣(原諒我的盜圖):
在不同的屏幕上(普通屏幕 vs retina屏幕),css像素所呈現的大小是一致的,不同的是1個css像素所對應的物理像素個數是不一致的。
在普通屏幕下,1個css像素 對應 1個物理像素(1:1
)。 在retina 屏幕下,1個css像素對應 4個物理像素(1:4
)。
5、位圖像素
一個位圖像素是柵格圖像(如:png,jpg,gif等)最小的數據單元。每一個位圖像素都包含了一些自身的顯示信息(如:顯示位置,顏色值,透明度等)。
理論上:一個位圖像素對應於1個物理像素,圖片才能得到完美清晰的展示。。
普通屏幕沒有問題的,但是在retina屏幕下會出現位圖像素點不夠,從而導致圖片模糊的情況。
看一張圖:
如圖所示:對於dpr=2的retina屏幕而言,1個位圖像素對於4個物理像素,由於單個位圖像像素不可以在分隔,只能就近取色,從而導致圖片模糊。
所以對於圖片高清的問題,比較好的方案是兩倍圖片(長寬增大到2倍)。
如:200×300(css pixel)img標籤,就需要提供400×600的圖片。
如此下來,位像素點個數就是原來的4倍,在retina屏幕下,位圖像素點個數就可以跟物理像素點個數形成1:1的比例,圖片自然就清晰了,解決了第一個疑問。
如果出現,普通屏幕下,也用了兩倍圖片,會怎麼樣?
很明顯,在普通屏幕下,200×300(css pixel)img標籤,所對應的物理像素個數就是200×300
個,而兩倍圖片
的位圖像素個數則是200×300*4
,所以就出現1個物理像素點對應4個位圖像素點,它的取色也只能通過一定的算法(顯示結果就是一張只有原圖像素總數四分之一,我們稱這個過程叫做downsampling(
降採樣,降低數據採樣率或分辨率的過程)
).肉眼看上去雖然圖片不會模糊,但是會覺得圖片缺少一些銳利度,或者是有點色差(但還是可以接受的)。
四、總結
移動端H5開發,在不同的分辨率,不同屏幕的手機下會遇到幾個經典問題
1、retina下,圖片高清的問題
上面已經講到了解決方案:圖片的長寬的兩倍,然後圖片容器縮小50%。
比如:
1、img 標籤
width: 200px;
height: 300px;
2、背景圖片
width: 200px;
height: 300px;
background-image: url(image@2x.jpg);
background-size: 200px 300px; // 或者: background-size: contain;
這樣的缺點就是,普通屏幕下:
1、同樣下載長度都2倍的圖片,造成了資源浪費。
2、圖片由於downSampling,會失去一些銳利度(或者色差)。
所以最好的解決辦法:不同的dpr下,加載不同的尺寸的圖片。
不管是通過css媒體查詢,還是通過js條件判斷都是可以的。所以需要準備兩套圖片。
2、retina下,border:1px的問題
1px是設計師用他的像素眼,最關心的問題。
還是通過一張圖來解釋(原諒我再次盜圖):
上圖中,對於一條1px
寬的直線,它們在屏幕上的物理尺寸(灰色區域)的確是相同的,不同的其實是屏幕上最小的物理顯示單元,即物理像素,所以對於一條直線,iphone5它能顯示的最小寬度其實是圖中的紅線圈出來的灰色區域,用css來表示,理論上說是0.5px
。
所以,設計師想要的retina下border: 1px;
,其實就是1物理像素寬,對於css而言,可以認爲是border: 0.5px;
,這是retina下(dpr=2)下能顯示的最小單位。
然而,無奈並不是所有手機瀏覽器都能識別border: 0.5px;
,ios7以下,android等其他系統裏,0.5px會被當成爲0px處理,那麼如何實現這0.5px
呢?
最簡單的一個做法就是這樣(元素scale
):
.scale{
position: relative;
}
.scale:after{
content:"";
position: absolute;
bottom:0px;
left:0px;
right:0px;
border-bottom:1px solid #ddd;
-webkit-transform:scaleY(.5);
-webkit-transform-origin:0 0;
}
我們照常寫border-bottom: 1px solid #ddd;
,然後通過transform: scaleY(.5)
縮小0.5倍來達到0.5px
的效果,但是這樣hack實在是不夠通用(如:圓角等),寫起來也麻煩。
或者可以使用js來判斷當前瀏覽器是否支持border的0.5px,如果支持就會在HTML上添加一個class名hairlines,
if (window.devicePixelRatio && devicePixelRatio >= 2) {
var testElem = document.createElement('div');
testElem.style.border = '.5px solid transparent';
document.body.appendChild(testElem);
if (testElem.offsetHeight == 1)
{
document.querySelector('html').classList.add('hairlines');
}
document.body.removeChild(testElem);
}
css
div {
border: 1px solid #bbb;
}
.hairlines div {
border-width: 0.5px;
}
五、參考
1、http://www.smashingmagazine.com/2012/08/20/towards-retina-web/
2、http://www.paintcodeapp.com/news/iphone-6-screens-demystified
3、http://iconmoon.com/blog2/iphone-6-plus-screen-size/
4、http://dieulot.net/css-retina-hairline