這個話題有些複雜,提及來有些瑣碎,由於和移動端適配相關的問題太多了。javascript
1. 概念css
1.1 設備像素html
設備像素被稱爲物理像素,它是顯示設備中一個最小的物理部件。每一個像素能夠根據操做系統設置本身的顏色和亮度。這些設備像素的微小距離欺騙了咱們肉眼看到的圖像效果。前端
1.2 屏幕密度(ppi)java
屏幕密度是指一個設備表面上存在的像素數量,它一般以每1英寸上排列有多少像素來計算(ppi:Pixels Per Inch)。蘋果公司聲稱人類的肉眼沒法區分單個像素,當一個顯示器像素密度超過300ppi的時候,肉眼就沒法區分出單獨的像素。也就是說設備清晰度超過人的視網膜能夠分辨像素的極限。所以手機顯示器的像素密度達到或超過300ppi就不會出現顆粒感,平板電腦像素密度達到或超過260ppi就不會再出現顆粒感。蘋果公司給這類設備起了一個科技感十足的名字「Retina」屏,即視網膜屏。android
1.3 css像素ios
css是一個相對的單位,主要使用在瀏覽器上,用來精確地度量web頁面上的內容。通常狀況,css像素被稱爲與設備無關的像素(device-independent像素),簡稱dips,也叫設備獨立像素,名字多,容易混淆。在一個標準顯示密度下,一個css像素對應着一個設備像素。css3
<div height="200" width="300"></div>
上面的代碼重定義了一個爲200px,高爲300px的div盒子。可是在Retina屏下,相同的div卻使用了400X600的設備像素保持相同的物理尺寸顯示,致使每一個css像素點實際上有4倍的普通像素點。git
若是咱們把這個寬,高屬性設置在一張圖片上,以下css設置github
img { width: 200px; height: 200px; }
在Retina屏中,這樣一個css像素點實際上分紅了4個設備像素點,形成顏色會近似選取,因而形成咱們看上去變得模糊。
所以在Retina屏上要想清晰地還原圖片,須要一個原始尺寸爲400X400css像素的圖片,而css像素仍然是200X200。以下:
此外還可使用device-pixel-ratio來處理,下面內容將會介紹。
dips(dip或者dp或者divice independent pixels)
設備獨立像素(也叫密度無關像素),能夠認爲是計算機屏幕座標系統中的一個點,這個點表明一個能夠由程序使用的虛擬像素(好比:css像素)而後由系統轉換爲物理像素。dips能夠用來輔助區分視網膜設備。視網膜設備是指分辨率達到300ppi的屏幕。
1.4 dpr(window.divicePixelRatio)
divicePixelRatio是設備上物理像素和設備獨立像素(dips)的比例,即window.divicePixelRatio = 物理像素/dips。以iphone4s爲例說明,它是「Retina屏」,分辨率爲960x640,以兩者較小的屏幕寬度來計算,物理像素640px,設備獨立像素320px,那麼window.devicePixelRatio = 640px / 320px = 2
1.5 Retina屏高清圖片模糊問題
爲了提高用戶體驗,節省移動端流量,針對不一樣的顯示屏,採用不一樣的方案,保證圖片在不一樣顯示屏幕下正常顯示。這裏採用的方法和原生app中針對不一樣分辨率採用不一樣圖片的原理相似。有如下兩點:
a. 普通顯示屏(devicePixelRation = 1.0, 1.3)加載一張1倍的圖片
b. Retina顯示屏(divicePixelRation >= 1.5, 2.0, 3.0 )加載一張2倍大小的圖片
利用媒體查詢結合devicePixelRatio能夠區分普通顯示屏和高清顯示屏
.css{/* 普通顯示屏(設備像素比例小於等於1.3)使用1倍的圖 */ background-image: url(img_1x.png); } @media only screen and (-webkit-min-device-pixel-ratio:1.5){ .css{/* 高清顯示屏(設備像素比例大於等於1.5)使用2倍圖 */ background-image: url(img_2x.png); } }
也可使用image-set加載不一樣圖片,image-set,是webkit的私有屬性,也是css3的一個屬性,目前已經有部分瀏覽器支持。它是爲了解決Retina屏幕下的圖像顯示而生。
.css{ background: url(../img/bank_ico.png) no-repeat;/* 不支持image-set的顯示屏 */ background: -webkit-image-set( url(../img/bank_ico.png) 1x,/* 支持image-set的瀏覽器的[普通屏幕]下 */ url(../img/bank_ico_retina.png) 2x);/* 支持image-set的瀏覽器的[Retina屏幕] */ }
上面兩種方式都會使用兩套或者多套圖片,增長了工做量。
1.6 dip&dp
dip:divice independentpixels,設備獨立像素,一個基於density的抽象單位。不一樣設備下有不一樣的顯示效果,這個和設備硬件有關,在Android上開發的程序將會在不一樣分辨率的手機上運行。爲了讓程序外觀不至於相差太大,引入了dip的概念。例如定義一個10 X 10dip的矩形,在分辨率爲160dpi的屏幕上,恰好是10 x 10像素;而在240dpi的屏,則是15 x 15像素,換算公式爲pixs = dips * (density / 160),density就是屏幕的分辨率。
這裏注意dip與屏幕密度有關,而屏幕密度與具體的硬件有關,硬件設置不正確,有可能致使dip不能正產顯示。在屏幕密度爲160的顯示屏上,1dip === 1px,有時候屏幕分辨率很大,例如400 * 800,可是屏幕密度沒有正確的設置,好比仍是設置成160,那麼凡是使用dip的都會顯示太小。
dp:與密度無關的像素,同dip同樣是一種基於屏幕密度的抽象單位,在每英寸160點的顯示器上,1dp=1px。
全部非視網膜屏幕的iPhone在垂直屏幕顯示的時候,它的寬度爲320物理像素,經過meta改變viewport時
<meta name="viewport" content="width=device-width">
這時視窗佈局變成320px(不一樣於視覺區域寬度,不放大顯示的狀況下,二者大小一致) ,這樣整個頁面天然地覆蓋在屏幕上。這樣非視網膜屏幕的iPhone上,屏幕的物理像素320像素,獨立像素也是320像素,所以window.devicePixelRatio等於1。
而對於視網膜屏幕的iPhone,如iPhone4s,縱向顯示的時候屏幕物理像素640,一樣當用戶設置
<meta name="viewport" content="width=device-width">
這個時候,視區寬度並非640像素,而是320像素,這時爲了更好的閱讀體驗,更合適大小的文字。這樣物理像素是640像素,獨立像素仍是320px,所以window.divicePixelratio等於2。
1.7 位圖像素
位圖像素(Pixel)組成的,像素是位圖最小的信息單元,存儲在圖像柵格中(PNG, JPG, GIF)。每一個像素都具備特定的位置和顏色值。從左到右,從上到下的順序記錄圖像中的每個像素信息,如:像素在屏幕上的位置,像素的顏色等信息。位圖圖像是有單位長度內像素的多少來決定的。單位長度內像素越多,分辨率越高,圖像效果越好。
位圖也稱爲「位圖圖像」,「點陣圖像」,「數據圖像」,「數碼圖像」。基於圖像的格柵分辨率,圖像在web中是由css像素定義了一個抽象的大小。瀏覽器擠壓或者拉伸圖像都是基於其css高度或者寬度來呈現的過程。
當一個光柵圖像在標準設備下全屏顯示時,一位圖像素對應的就是一設備像素,致使一個徹底保真的顯示。由於一個像素不能進一步分裂,在Retina屏幕下,要放大四倍來保持相同的物理像素大小,這樣就會丟失不少的細節,形成失真的狀況。換句話說,每一位圖像素被乘以四填補相同的物理標籤在Retina屏幕下顯示。
1.8 高分辨率屏幕&高像素密度屏幕
在Retina視網膜屏幕面世以前不多有人關注像素密度,在windows系統下,提升屏幕分辨率通常都是經過提升屏幕尺寸。而隨着屏幕分辨率的提高,圖像和文字顯示目標會相應縮小,形成觀看不方便。由於系統不會自動根據屏幕尺寸和分辨率關係相應的調整文字和圖標大小。即手動調整也會由於微軟一直採用的點陣字體和大多數位圖在提升分辨率後,由於多餘的像素點沒有填充渲染會出現拉伸,進而會產生鋸齒,這是系統不會自動適配的緣由之一。這也給咱們形成一種假象:顯示器尺寸越大,分辨率就會越大。
因此蘋果的Retina視網膜屏幕讓不少人困惑不已,爲何那麼小的屏幕會有那麼大的分辨率。爲何那麼大的分辨率,非但沒有使文字和圖像變小,反而更加清晰。
1.9 高像素密度屏幕(高ppi)
嚴格來講,高像素密度屏幕也是屬於高分辨率屏幕,不一樣的是高像素密度屏幕在提高了分辨率的同時也提升了像素密度,即相同的尺寸有着更大的分辨率。以蘋果的Retina視網膜屏幕爲例,它並非普通屏幕那樣經過增大尺寸來增長分辨率,而是靠提高單位面積屏幕的像素數量,即像密度來提高分辨率,這樣就有了高像素度屏幕。
同時操做系統也會採起相應的模式,如Mac下的HiDPI進行適配,將縮小的字體(蘋果一直採用矢量字體)和圖標從新放大,這樣蘋果用了更多的像素來顯示一樣的內容,顯示尺寸仍然不變,可是多了更多細節,所以會有很是明顯的視覺效果提高。
iPhone/iPod Touch
普通屏幕分辨率 ====> 320px X 480px
Retion分辨率 ====> 640px X 960px
iPad,iPad2/New iPad
普通屏幕分辨率 ====> 768px X 1024px
Retion分辨率 ====> 1536px X 2048px
換算關係
普通屏幕分辨率 ====> 1點=1像素
Retion分辨率 ====> 1點=2像素
1.10 Retina設備
Retina是蘋果提出來的,根據蘋果的定義,PPI高於210(筆記本電腦) ,260(平板電腦),300(手機)的屏幕就成爲Retina屏
上圖是部分蘋果手機屏幕參數。
1.11 HTML和CSS的標準
儘管如今的顯示設備大部分仍是非Retina屏,可是如何使用Retina來優化web顯示的方法如雨後春筍,愈來愈多的處理方案被提出。
最直接的方式就是經過手動製圖或者編程的方式製做兩種不一樣的圖形,一張是普通屏幕的圖片,另外一種是Retina的圖形,而且Retina屏幕下的圖片像素是普通屏幕下的兩倍。若有一張200X300像素的圖片(css像素),而後製做一張400X600像素的圖片,經過css或者html屬性將其屬性壓縮50%,這時在一個標準分辨率的顯示器上,其呈現的圖像是位圖的四分之一像素。簡單的說,普通屏幕下的圖像被壓縮,減小像素取樣(只是位圖含像素的四分之一),同時,這樣會形成資源浪費。把這個過程稱爲「Downsampled」。
在Retina屏幕下,相同的圖像會使用四倍的物理像素顯示,這樣一來,每一個物理像素最終徹底配備一位圖像素,使圖像獲得徹底的呈現。
有幾種方法能夠實現這樣的效果。
1. HTML屬性
最簡單的方式是經過img標籤,設置width,height屬性:
<img src="example@2x.png" width="200" height="300" />
注意,即便指定的圖片高度是可選的,可是在加載圖片前,瀏覽器已經預留了所需的空間。這樣能夠防止頁面佈局更改圖片時,再加載一次。
2. 使用Javascript
一樣的效果,能夠經過Javascript腳本對圖像(爲Retina屏幕準備的圖像)進行減半。
$(window).load(function() { var images = $('img'); images.each(function(i) { $(this).width($(this).width() / 2); }); });
3. 使用css
另一種方法是經過css來實現。常見方式就是給元素設置一個背景圖像,好比給div元素設置一個背景圖像,最關鍵的是給這個背景圖像設置background-size,來指定背景圖像的大小,也能夠給元素設置一個大小,而後給background-size設置一個contain屬性值。不過IE7,IE8不支持這個屬性的運用。
.image { background-image: url(example@2x.png); background-size: 200px 300px; /*或者設置background-size: contain; */ height: 300px; width: 200px; }
還可使用元素的僞元素來代替
.image-container:before { background-image: url(example@2x.png); background-size: 200px 300px; content:''; display: block; height: 300px; width: 200px; }
HTML和CSS方法的
優勢是:
很容易實現;
能跨瀏覽器兼容;
缺點是:
非Retina屏下必須下載更大的圖片資源;
Downsampled圖像在不一樣的分辨率下可能會失去必定的清晰度;
background-size在IE9下瀏覽器下支撐不友好;
1.12 查詢圖像密度
爲Retina屏幕下查詢web圖像的像素密度,而後調用對應的圖像。目前經常使用的方式是經過css或者Javascript來實現。
1. 使用CSS Media Queries
經過divice-pixel-ratio屬性或者其擴展屬性min-device-pixel-ratio和max-device-pixel-ratio。這幾個Media Queries可使用background-image爲Retina準備高精度像素圖片。
經過window.devicePixelRatio的比例1.5或2爲不一樣的蘋果設備作相應的查詢。
.icon { background-image: url(example.png); background-size: 200px 300px; height: 300px; width: 200px; } @media only screen and (-Webkit-min-device-pixel-ratio: 1.5), only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (min-device-pixel-ratio: 1.5) { .icon { background-image: url(example@2x.png); } }
iPhone4
@media only screen and (-webkit-min-device-pixel-ratio : 1.5),only screen and (min-device-pixel-ratio : 1.5) { /* Styles */ }
Retina屏幕和普通屏幕
@media only screen and (min-width: 320px) { /* Small screen, non-retina */ } @media only screen and (-webkit-min-device-pixel-ratio: 2) and (min-width: 320px), only screen and ( min--moz-device-pixel-ratio: 2) and (min-width: 320px), only screen and ( -o-min-device-pixel-ratio: 2/1) and (min-width: 320px), only screen and ( min-device-pixel-ratio: 2) and (min-width: 320px), only screen and ( min-resolution: 192dpi) and (min-width: 320px), only screen and ( min-resolution: 2dppx) and (min-width: 320px) { /* Small screen, retina, stuff to override above media query */ } @media only screen and (min-width: 700px) { /* Medium screen, non-retina */ } @media only screen and (-webkit-min-device-pixel-ratio: 2) and (min-width: 700px), only screen and ( min--moz-device-pixel-ratio: 2) and (min-width: 700px), only screen and ( -o-min-device-pixel-ratio: 2/1) and (min-width: 700px), only screen and ( min-device-pixel-ratio: 2) and (min-width: 700px), only screen and ( min-resolution: 192dpi) and (min-width: 700px), only screen and ( min-resolution: 2dppx) and (min-width: 700px) { /* Medium screen, retina, stuff to override above media query */ } @media only screen and (min-width: 1300px) { /* Large screen, non-retina */ } @media only screen and (-webkit-min-device-pixel-ratio: 2) and (min-width: 1300px), only screen and ( min--moz-device-pixel-ratio: 2) and (min-width: 1300px), only screen and ( -o-min-device-pixel-ratio: 2/1) and (min-width: 1300px), only screen and ( min-device-pixel-ratio: 2) and (min-width: 1300px), only screen and ( min-resolution: 192dpi) and (min-width: 1300px), only screen and ( min-resolution: 2dppx) and (min-width: 1300px) { /* Large screen, retina, stuff to override above media query */ }
CSS Media Queries的優勢:
只有對應的目標元素纔會下載圖片資源;
跨瀏覽器兼容;
像素能夠精確控制;
CSS Media Queries的缺點:
單調無味的實現過程,代碼冗長;
只能經過HTML元素的背景圖片來實現,無語義化
2. 使用Javascript
使用Javascript中的window.devicePixelRatio進行判斷,而後根據對應的值給Retina屏選擇圖像。
$(document).ready(function(){ if (window.devicePixelRatio > 1) { var lowresImages = $('img'); images.each(function(i) { var lowres = $(this).attr('src'); var highres = lowres.replace(".", "@2x."); $(this).attr('src', highres); }); } });
開源的Retina.js是爲Retina而生,基本實現了上面全部功能。目前支持window.devicePixelRatio的瀏覽器很少,未來會有更多的瀏覽器添加這個api。
Javascript查詢的優勢:
易於實施;
非Retina屏幕不用下載額外的資源;
像素精確控制;
Javascript查詢的缺點:
Retina屏幕下必須下載標準準備和高密度的兩個資源;
Retina屏幕下圖像交互可見;
瀏覽器兼容性不強;
1.14 可縮放矢量圖形
無論用什麼方式,光柵圖像仍然有本身固定的圖像分辨率,也就是其縮放始終受限於其像素,也絕對沒法限制伸縮。可是矢量圖就不同,能夠隨意伸縮,並且無任何影響。在Retina屏幕下的web圖形,矢量圖具備先天優點。
目前,基於XML的svg格式製做的矢量圖獲得大部分瀏覽器的支持,可使用svg繪製各類圖形,而且在Retina屏幕下能夠任意伸縮。開發過程當中使用svg最簡單的方法是經過HTML的img標籤,CSS的background屬性或者微元素的content中的url()。
HTML的img標籤加載svg
<img src="example.svg" width="200" height="300" />
在這裏是一個svg凸顯更能夠爲普通屏幕和Retina屏幕的通用圖像,能夠作任何伸縮,而不會像光柵位圖同樣失真,並且資源統一,節省帶寬,方便維護。
CSS中使用svg圖像
svg圖像能夠像普通圖像一眼個,看成元素的背景圖片來使用:
.image { background-image: url(example.svg); background-size: 200px 300px; height: 200px; width: 300px; }
除了看成背景圖片以外,還能夠經過僞元素的「content」來調用:
.image-container:before { content: url(example.svg); }
若是想在IE7,IE8和Android2.x上使用,要用png圖片來代替svg圖像
.image { background-image: url(example.png); background-size: 200px 300px; } .svg { .image { background-image: url(example.svg); } }
在HTML標籤中,給img標籤訂義一個屬性,給這個自定義屬性設置一個png圖片,以作備用,不過這種方式須要使用Javascript配合使用。
$(document).ready(function(){ if(!Modernizr.svg) { var images = $('img[data-png-fallback]'); images.each(function(i) { $(this).attr('src', $(this).data('png-fallback')); }); } });
SVG矢量圖的優勢:
一個資源適合全部的設備
易於維護
面向將來,可伸縮向量圖形
SVG矢量圖的缺點:
沒有像素那樣有精度
因爲文件大小,不適合複雜的圖形
不支持IE7-8和部分安卓版本
最後開發人員如何讓網站適應Retina屏幕呢,看下面的總結:
2. H5頁面適配的一些基本概念
2.1 視窗viewport
viewport的功能在於控制網站最頂層容器,即html元素。假設咱們定義一個可變尺寸的佈局,且定義了一個側邊欄的寬度爲10%,當改變瀏覽器窗口大小時,改側邊欄會自動擴展個收縮。這是什麼原理呢?側邊欄寬度是父元素寬度的10%,假設父元素是<body>,那麼問題就變成了<body>的寬度究竟是多少?一般一個塊元素佔父元素100%的寬度,因此body元素的寬度就是其父元素<html>的寬度。
那麼<html>的寬度又是多少呢?它的寬度是瀏覽器的寬度。因此側邊欄寬度10%會佔用10%的瀏覽器寬度。<html>的寬度受viewport所限制,<html>元素爲viewport寬度的100%。
反過來,viewport是嚴格的等於瀏覽器的窗口。viewport不是一個html概念,因此它不會被CSS屬性修改。它就是爲瀏覽器窗口的寬度高度,在桌面瀏覽器上是如此,在移動端有些複雜。
簡單的理解,viewport是嚴格等於瀏覽器窗口。在桌面瀏覽器中,viewport就是瀏覽器窗口的寬度高度。可是在移動端有些複雜,移動端viewport太窄,爲了更好的僞css佈局服務,因此提供了兩個viewport:虛擬的,visual viewport和佈局的layout viewport。
設備的pixels和css的pixels
相對css像素來講,設備像素的概念比較單一。咱們的網頁在什麼樣的設備上運行呢?設備像素給出了這個答案,能夠從window.screnn.width/height(大多數狀況下)讀到。
設備的pixels和設備自己有關,簡單來講就是window.screen.width/window.screen.height,能夠認爲設備的pixels是正確,標準的寬和高,這些pixels決定了設備上正式的分辨率。
假設設置頁面中一個元素的寬度是128px,顯示器的寬度是1024px,且沒有拖拉顯示器的右上角是的顯示器變小,那麼能夠在瀏覽器中放置8個這樣的元素(忽略掉margin,padding等這些概念)。
若是用戶縮放(zoom)了瀏覽器,狀況就改變了。若是瀏覽器放大到200%,結果是隻能在瀏覽器中放置4個這樣的元素來佔滿顯示器1024的寬度。
現代瀏覽器都實的「縮放」(zoom)功能,除了能夠「拉伸」像素以外沒有什麼做用,結果是html元素的寬度沒有由於縮放200%而由128pix變成256px,而是實際的的pixels被計算成了雙倍。html元素在形式上依然是128css的pixels,可是它佔用了256設備pixels。換言之,縮放200%將一個單位的css的pixels變成了4倍的設備的pixels那麼大,即寬度*2,高度*2,面積*4。
用一些圖片來講明這個概念,下面圖片中有4個正常縮放(100%縮放)的像素,縮放爲100%的html元素,css的pixels完整和設備的pixels重疊。
當咱們縮小瀏覽器時,css的pixels開始收縮,致使1單位的設備pixels上重疊了多個css的pixels,以下:
同理,放大瀏覽器的時候,相反的事情發生了,CSS的pixels開始放大,致使1單位的CSS的pixels上重疊了多個設備的pixels。
問題在於,設備的pixels幾乎毫無用處,開發者只關注css的pixels,這些pixels指定了html元素如何渲染。可是對用戶來講是另一回事,用戶會縮放界面,直至能夠溫馨的閱讀網頁內容,可是開發者不須要關注這些縮放級別,瀏覽器會自動的保證css的pixels會被伸展仍是收縮。
100%縮放
在縮放級別是100%的時候,1單位的CSS的pixels是嚴格等於1單位的設備pixels。
100%縮放的概念很是有利於表達下面的問題,在平常開發中沒必要過度擔心這個問題。在桌面系統中,一般會在100%縮放級別下測試網站,即便用戶縮放,css的pixels的魔法依然能夠保證網站外觀保持相同的比例,開發者沒必要過度擔憂。
屏幕尺寸 Screen size
screen.width/height
含義:用戶的屏幕的完整大小
度量:設備的pixels
兼容性:IE8裏,無論使用IE8模式仍是IE7模式,都是以CSS的pixels來度量
這兩個屬性包含用戶屏幕的完整高度和寬度。這些尺寸使用設備的pixels來定義,他們的值不會由於縮放而改變,他們是顯示器特徵,而不是瀏覽器。咱們能夠拿着個來作什麼呢?除了獲取用戶屏幕信息以外用到的地方不多。
瀏覽器尺寸window size
window.innerWidth/window.innerHeight
含義:包含滾動條尺寸的瀏覽器完整尺寸
度量:CSS的pixels
兼容性問題:IE不支持,Opera用設備pixels來度量
瀏覽器內部尺寸,很明顯它定義了當前用戶有多大區域可供CSS佈局使用。能夠用window.innerWidth和window.innderHeight來獲取。窗口的內部寬度使用CSS的pixels,開發時須要知道本身定義的元素是否能塞入瀏覽器窗口,這兩個尺寸會隨着用戶放大(縮放)瀏覽器而減小。因此當用戶放大顯示時,能獲取的瀏覽器窗口可用空間會減小。window.innerWidth/window.innerHeight就是縮小的比例。
(在Opera瀏覽器上有些異常,window.innerWidth/innerHeight不會隨着用戶放大(縮放)瀏覽器而減少,它以物理像素衡量。這在桌面端很是討厭,可是在移動端會帶來致命的結果,稍後討論)
注意,窗口內部寬度和高度的尺寸,包含了滾動條的尺寸,這是歷史緣由。
滾動移位 Scrolling offset
含義:頁面的移位
度量:CSS的pixels
兼容性問題:window.pageXOffset和window.pageYOffset,在IE8及以前的IE瀏覽器不支持。使用document.body.scroolLeft和document.body.scrollTop來取代
window.pageXOffset和window.pageYOffset定義了頁面(document)相對於窗口原點的水平,垂直位移。所以能夠定位用戶滾動了多少滾動條距離。注意由於頁面中的內容是能夠無限添加的,頁面滾動的距離可能比頁面實際可見的高度要大得多。
理論上,若是用戶滾動並放大(縮放)瀏覽器,window.pageXOffset/pageYOffset會改變,可是,現代瀏覽器會盡可能保持在屏幕上方被捲入的元素有相同的尺寸。實際狀況是,這不必定都會起做用,實際上,一般狀況下,window.pageXOffeset/pageYOffset並不會改變。
該屬性也以CSS的pixels來度量,同上面的問題,當用戶放大頁面時,瀏覽器會嘗試着保存用戶當前可見的頁面的元素依然在可見位置。雖然這個特性表現的不如預期,可是它意味着,理論狀況下window.pageXOffset/window.pageYOffset並無改變,被用戶滾出屏幕的CSS的pixels幾乎保持不變。
概念:視窗viewport
viewport的功能在於控制網站的最高級別的塊容器,<html>元素。舉個例子,定義個一個可變尺寸的佈局,且定義了一個側邊欄尺寸爲10%,當改變瀏覽器窗口大小的時候,該側邊欄會自動擴張和收縮。側邊欄是父元素body寬度的10%,那麼body的寬度又是多少呢?一般一個塊級元素佔有父元素的100%的寬度,因此body的寬度就是其父元素html的寬度。那麼html元素到底有多寬?由於它的寬度剛好是瀏覽器的寬度。因此側邊欄的寬度就是瀏覽器寬度的10%。一般是這麼個道理,實際上html寬度受viewpoint所限制,html元素爲view寬度的100%。
viewpoint被定義成嚴格等於瀏覽器的窗口,它不是一個html的概念,不能經過css定義修改它。在PC端他的寬度和高度嚴格等於瀏覽器的高度,在移動端,狀況有些複雜。
結果
viewport的定義會帶來一些奇怪的後果。縮小(拖動瀏覽器右上角)瀏覽器,按住Ctrl鍵,向上滾動鼠標滾輪2至3格,放大(縮放)瀏覽器,拖動滾動條到網頁頂部,會發現藍色的頁眉有些錯亂,沒有佔滿整個瀏覽器。以下圖:
這是因爲viewport的侷限帶來的後果。頂部藍色頁眉的寬度是這樣定義的width:100%; 它的寬度應該是html元素的100%,viewport的寬度又是真個瀏覽器寬度的100%。
關鍵在於,在瀏覽器100%大小(縮放)狀況下徹底沒有問題,如今放大(縮放)了。viewport變得比網站寬度小(這時css像素比物理像素要小,瀏覽器的寬度已經變窄了(拖放),頁眉的寬度要比瀏覽器小),,overflow:visible;元素默認超出父元素時正常顯示。
頁眉並無溢出,是由於頁眉的寬度css定義爲width:100%,即爲viewport寬度的100%,它沒有意識到由於這時viewport的寬度已經變窄了。
document width
網頁文檔的寬度,包含溢出的那部分是多少呢?如今沒有一個api可以告訴咱們,除非計算出每一個元素的寬度,包含margin,padding,border等諸多因素,這是很是容易出錯的。
若是能有一個javascript屬性可以告訴咱們這個document width就行了。若是有一個文檔寬度,就能夠設置藍色頁眉的寬度爲文檔寬度的100%。
度量viewport
含義:viewport的尺寸
度量:css的pixels
兼容性問題:無
viewport的尺寸能夠用document.documentElement.clientWidth/clientHeight來獲取。document.documentElement實際表明整個網頁的根元素:html元素。實際這對屬性描述的是viewport的尺寸,它描述的是viewport的尺寸,而無論html元素的尺寸如何改變。
描述viewport的兩種方式
上文中已經說了,window.innerWidth/innerHeight是viewport的寬度和高度嗎?嗯, 是, 也不是。嚴格說這兩對屬性有細微的差異。
document.documentElement.clientWidth/clientHeight是viewport的不包含滾動條長度的寬度和高度
window.innerWidht/innerHeight是viewport的包含滾動條長度的寬度和高度
之因此會出現兩套屬性來描述viewport的寬度和高度是瀏覽器大戰留下的遺物。那個時候網景公司支持window.innerWidth/innerHeight,IE只支持document.documentElement.clientWidth/clientHeight,儘管其餘瀏覽器都開始支持clientWidth/clientHeight,可是IE仍然沒有支持,僅僅支持innerHeight/innerWidth。
桌面端有兩種方式獲取viewport的尺寸是一個困惑,可是在移動端確是一個福利。
度量html元素
document.documentElement.offsetWidth/offsetHeight用來表示html元素的尺寸,包含看不見的部分。
含義:<html>的尺寸
度量:css的pixels
兼容性問題:IE用這個描述viewport的尺寸而不是<html>
用這一對屬性能夠獲取html元素的寬度和高度,能夠經過設置html元素的css屬性來改變它。
事件座標
含義:當發生鼠標事件的時候,至少會有5個屬性來暴露當前鼠標的位置信息,下面來看其中的個屬性
度量:見下文
兼容性問題:IE不支持pageX/pageY,IE使用screenX/screenY,css的pixels
定義:1. pageX/pageY描述事件相對於html元素的座標,css的pixels
2. clientY/clientY描述事件相對於viewport的座標,css的piexles
3. screenX/screenY描述事件相對於屏幕的座標,css的pixels
一般咱們會有90%的可能來使用pageX/pageY,即相對於html元素的尺寸,其他10%的可能會使用clientX/clientY,即相對於viewport的尺寸,screenX/screenY這對屬性一般沒有用。
媒體查詢
含義:見下文
度量:見下文
兼容性:IE不支持媒體查詢,在firefox中,使用screen.width/height,而不是divice-width/divice-height,使用css的pixels;對於width/height,Safari和chrome使用document.documentElement.clientWidth/clientHeight的值(若是他們是以設備像素爲單位的話)
媒體查詢的思路是很簡單的,定義在不一樣頁面尺寸下的css的規則,好比大於,小於,等於某一個尺寸,看下面的例子:
div.sidebar { width: 300px; } @media all and (max-width: 400px) { // styles assigned when width is smaller than 400px; div.sidebar { width: 100px; } }
當前sidebar是300px,當寬度小於400px的時候變成100px。
問題是以哪一個寬度爲依據的呢?有兩類媒體查詢,widht/height,divice-width/divice-height。
1. width/height使用的是document.documentElement.clientWidth(viewport,不帶滾動條),使用css的pixels
2. divice-width/divice-height使用的是screen.width/screen.height(顯示器的寬度和高度),使用物理像素
毫無疑問,咱們要使用with/height,顯示器寬度和高度對開發者來講意義不大,瀏覽器開發商纔會關心這個。在移動端,狀況會稍微簡單點,接下來討論這些問題。
移動端瀏覽器的問題
和pc端相比,移動端最大的不一樣是屏幕尺寸。移動端尺寸要小,顯示的內容也要比pc端少,要麼縮小致使網頁內容難以辨認,要麼只顯示網頁中的一小部分。
移動端屏幕要比pc端小不少,一般最大也就400px,當前雖然有些製造商聲稱他們的屏幕很大,可是他們每每誇大其詞。一些折中的方案,好比ipad或者其餘的平板電腦介於手機和pc之間,可是這些也沒有根本解決問題。移動端大潮不可避免,網頁必須能在移動端工做,因此千方百計是得網頁在移動端同樣表現良好。
大多數問題集中在css上,特別是viewport的度量上。若是咱們簡單地把pc端網頁拷貝到移動端,樣式將變得奇醜無比。
這裏咱們繼續用sidebar的寬度10%這個問題來討論。若是移動端行爲和pc端同樣,那麼這個sidebar最多也只有40px,這個尺寸就過小了。一種解決方式是爲移動端再作一個網站。暫且不說這個網站要怎麼作,一般只有少部分公司纔會投入成原本作這件事,爲移動端單獨作一個網站。移動瀏覽器廠商則但願他們的軟件提供更好的體驗,「儘可能像pc端同樣的體驗」,因此他們就會無所不盡其能。
兩種viewports
移動端的viewport對於css佈局來講很是小,一種解決方案是把viewport變得大一點。這種需求就把viewport分紅兩種:虛擬viewport,佈局viewport。
George Cummins(喬治.康明斯,不知道是誰,應該是前端大牛把)對着兩種viewport給出了這樣的解釋:
能夠把佈局viewport想象成一個不能改變大小和形狀的圖片。如今用一個小一點的框來觀察這個大圖片。這個小框有被不透明的材料包裹,所以經過這個框只能觀察到這個圖片的一部分。經過這個框可以觀察到的部分圖片就是虛擬viewport,若是咱們後移這個框,站遠一點,就能看到整張圖片了,至關於放大(縮放)了這個框。你也能夠改變這個框(可視viewport)的方向,可是後面的圖片(佈局viewport)是不會改變的。
虛擬viewport是咱們當前可以在移動端顯示器上看到的部分。用戶可能會滾動來改變可見部分,放大,縮小,均可以修改這個可視viewport。
可是,css佈局中,尤爲是百分比寬度,是按照佈局layout來計算的,這樣一來就要比按照可視viewport計算要寬得多了。
因此,默認狀況下,html元素繼承了佈局viewport的寬度,css佈局或按照這個寬度來計算,這使得網站在移動端儘可能貼近pc端樣式。
佈局viewport有多寬呢?根據瀏覽器不一樣而不一樣,Safari iPhone是980px,Opera是850px,AndroidWebkit是800px,IE是974px。部分瀏覽器行不一樣:
縮放
兩種佈局都是以css的pixels來度量的,可是當縮放比例變化的時候可視viewport也會改變(縮放比例變大的時候,一個css的pixels會佔據多個物理的pixels,可視範圍內css的pixels變少),佈局viewport的度量保持不變。這是頁面會重繪,元素的百分比尺寸會被從新計算。
理解佈局viewport
爲了理解佈局layout的尺寸,咱們先看看當王爺縮小到時候回有什麼狀況發生。許多移動端瀏覽器默認以最小縮放比例打開王爺,這樣能夠看到王爺全部內容。關鍵點在於,瀏覽器選擇最小縮放比例模式,以便屏幕可以顯示全部的網頁。
所以,佈局視圖的寬度和高度和最小縮放模式下在屏幕中可以顯示的內容的寬和高。當用戶放大(縮放)的時候,保持不變。
佈局視圖不變,可是在旋轉(橫放)模式下可視viewport改變,瀏覽器經過稍微放大以適應這一新方向,這樣佈局viewport再次與可視viewport再次保持一致。
這對佈局viewport有一些影響,如今的高度比縱向模式要小得多。可是開發人員不會關心屏幕高度,只關心寬度。
度量視圖viewport
document.documentElement.clientWidth/clientHeight
含義:佈局viewport的尺寸
度量:css的pixels
完整支持:Opera,iPhone,Android,Symbian,Bolt,MicroB,Skyfire,Obigo
問題:
很幸運,雖然有兩種viewport,瀏覽器大戰留下了兩對屬性來衡量他們。document.documentElement.clientWidth/clientHeight表明可視viewport的度量
旋轉模式下隻影響到高度,不影響寬度。
度量可視viewport
window.innerWidth/innerHeight
含義:可視viewport的度量
度量:css的pixels
完整支持:iPhone,Symbian,BlackBerry
問題:
可視viewport使用window.innerWidth/innderHeight來衡量。顯然,隨着用戶縮放瀏覽器,這個值會改變,屏幕上的css的pixels會改變。
不幸的是,這對屬性兼容性不好,不少瀏覽器沒有支持,希望未來有更多瀏覽器實現它,使它成爲一個標準。
屏幕尺寸度量
screen.width/height
含義:屏幕的尺寸;
度量:物理的pixels;
徹底支持:Opera,Android,Symbian,Iris,Firefox,MicroB,IE,BlackBerry;
問題:
pc端中screen.width/height給出的是屏幕尺寸,用物理pixels衡量,做爲web開發者,這個不經常使用。一樣在移動端,你不用屏幕上有多少物理pixels,只關心有多少css的pixels。
縮放等級zool level
沒法直接獲取縮放等級,可是能夠經過計算獲取:screen/width/window.innerWidth,固然只有在這兩個屬性都被支持的狀況下才能這樣獲取。幸運的是,縮放等級並不重要,只須要知道當前有多少css的pixels能供使用就能夠了。在瀏覽器支持的狀況下,使用window.innerWidth獲取這一信息。
滾動scrolling offset
window.pageXOffset/pageYOffset
含義:見下文;
度量:css的pixels;
完整支持:iPhone,Android,Symbian,Iris,MicroB,Skyfire,Obigo;
問題:
一般還要知道當前可視viewport相對於佈局viewport的位置,這就是滾動,和pc端同樣,能夠經過window.pageXOffset/pageYOffset獲取。
<html>元素的度量
document.documentElement.offsetWidth/offsetHeight
含義:真個html元素的尺寸
度量:css的pixels
完整支持:Opera, iPhone, Android, Symbian, Samsung, Iris, Bolt, Firefox, MicroB, Skyfire, BlackBerry, Obigo
問題:只有在100%縮放下NetFront瀏覽器返回的值纔是正確的;IE使用這個屬性來輸出可視viewport,html元素尺寸使用document.body.clientWidth/clientHeight來輸出;
和桌面端同樣,移動端也使用document.documentElement.offsetWidth/offsetHeight來表示html元素的尺寸,用css的pixels度量。
媒體查詢
含義:已css的pixels度量html元素或者已設備的pixels來度量設備;
完整支持:Opera, iPhone, Android, Symbian, Samsung, Iris, Bolt, Firefox, MicroB;
不支持:Skyfire, IE, BlackBerry, NetFront, Obigo;
注意:這裏測試了瀏覽器是否明確地出了這些值,這些值的準確性沒有測試;
移動端媒體查詢和桌面端相似,width/height使用以css的pixels度量的佈局viewport,divice-width/divice-height使用物理pixels來度量。換句話說width/height就是document.clientWidth/clientHeight,divice-width/divice-height就是screen.width/height(全部瀏覽器都是這樣,即便屬性的值是錯誤的)
開始我認爲divice-width/divice-height比較重要,由於它給出了咱們所使用設備的屏幕的尺寸信息。例如,能夠根據設備尺寸來設置不一樣的網頁尺寸。可是一樣能夠定義<meta viewport>來達到這個目的。因此divice-width/divice-height不是不可替代的。
瀏覽器廠商自認爲他們經過width/height給出了一些對網站有用的尺寸細節,可是這些內容含糊,混亂。因此媒體查詢在區分當前設備是移動設備(手機,pad,pda)仍是pc誰被時頗有用,可是在區分是當前手機(或pad,pda)的尺寸上用處沒有那麼大。
事件座標event coordinates
含義:見下文
度量:見下文
完整支持:Symbian,Iris
問題:
Opera移動端給出了pageX/pageY,可是當屏幕滾動查出範圍時,這個值是錯誤的;
在iPhone,firefox,blackberry上,clientX/clientY和pageX/pageY相等的;
在Android和MicroB上screenX/screenY和clientX/clientY是相同的,css的pixels度量;
在Firefox上screenX/screenY是錯誤的;
IE,blackberry,Obigo不支持pageX/pageY;
在NetFront上三種尺寸的值都用screenX/screenY;
在Obigo上clientX/clientY是用screenX/screenY來表示;
在三星webkit瀏覽器上老是返回pageX/pageY;
時間座標在pc端上大體是支持的,不幸的是移動端上只有symbian,Irias徹底支持,其餘的移動端瀏覽器或多或少都有些問題。
pageX/pageY依然是基於css的pixels度量,和pc端瀏覽器刪狗同樣,它是三個特性裏面最有用的。
clientX/clientY是聚義可視viewport的,以css的pixels爲度量的,這樣比較可靠。screenX/screenY基於屏幕的物理的pixels爲度量的。一樣,它也是沒有什麼用的。
meta viewport標籤
含義:設置可視viewport的寬度
度量:css的pixels
完整支持:Opera Mobile, iPhone, Android, Iris, IE, BlackBerry, Obigo
不支持:Opera Mini, Symbian, Bolt, Firefox, MicroB, NetFront
問題:
最後,咱們討論<meta name="viewport" content="width=320">這個標籤。起初,它是apple的一個擴展,後來不少瀏覽器都實現了這個擴展。它意圖重置可視viewport,下面咱們來討論爲何會出現這個擴展。
假設咱們有一個簡單的網頁,全部元素都沒有設置css寬度。如今設置縮放比例爲100%,大多數瀏覽器會在屏幕上顯示全部佈局viewport,以下:
當咱們放大(縮放)瀏覽器的時候,大多數瀏覽器都保存元素完整的寬度(保持元素位置不變)致使閱讀困難(文字超過屏幕)。
(Android瀏覽器有些特殊,它會減少包含文字的元素的尺寸,保證他們仍然徹底顯示在瀏覽器上,我以爲這個特性很酷,其餘瀏覽器應該學習)
如今設置html元素的樣式{ width: 320px },如今html元素收縮,其餘元素經過100%繼承320px。在用戶放大(縮放)的狀況下正常,可是在最初期狀態下縮小(縮放)的時候頁面幾乎沒有什麼內容,體驗很糟糕。
爲了解決這個問題,apple發明了meta視圖標籤,當設置標籤<meta name="viewport" content="width=320">,能夠將佈局viewport設置爲320px,如今初始狀態下頁面的顯示就變得正常了。
經過這個標籤能夠將佈局viewport的寬度設置爲你須要的任意尺寸,甚至是divice-width。經過參考screen.width(物理的pixels)能夠設置佈局viewport尺寸。
可使用鉤子,有時候嚴格的screen.width一點意義都沒有,由於pixels的值太大。例如Nexus One上的嚴格寬度是480px,可是google的工程師以爲在用戶誰知device-width的時候,設置爲480太大了,他們將這個值減少爲原來的2/3,爲320px,和iPhone上一致。
3. 使用Flexible實現H5適配
3.1 痛點
雖然h5的頁面比pc端簡單很多,可是要想匹配全部的設備是一件很是痛苦的事情,由於移動端設備太多了。
3.2 flexible適配方案
爲了適應這麼多的移動端誰被,設計師和前端開發以前應該採用什麼樣的協做模式呢?手淘團隊曾經的協做思路是:
看下圖:
手淘設計師一般會選擇iPhone6做爲基準設計尺寸,交付給前端開發的設計尺寸是按照750px/1334px爲準,固然高度會隨內容增多和改變。前端開發經過一套適配規則自動適配到其餘的尺寸。通過多年的摸索實踐,總結了一套移動端適配方案—flexible方案。
3.3 使用方法
lib-flexible庫的使用方法很是簡單,只須要在web頁面的<head></head>之間添加對應的flexible_css.js,flexible.js文件,能夠直接下載文件,而後放在項目目錄中:
<script src="build/flexible_css.debug.js"></script> <script src="build/flexible.debug.js"></script>
可使用CND
<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.4/??flexible_css.js,flexible.js"></script>
強烈建議對js作內聯處理,在全部資源加載以前執行這個js。執行以後會在<html>元素上增長一個data-dpr屬性,以及一個font-size樣式。JS會根據不一樣的設備添加不一樣的data-dpr值,好比2或者3,同時給html加上對應的font-size的值,好比說75px。
如此一來,頁面的元素均可以經過rem單位來設置,他們會根據html的font-size值作相應的計算,從而實現屏幕的適配效果。
除此以外,在引入lib-flexible須要執行的js以前,能夠手動設置mea來控制dpr的值。例如:
<meta name="flexible" content="initial-dpr=2" />
其中initial-dpr強制設置爲給定的值。若是手動設置了dpr以後,不過設備的dpr是多少,都會強制認爲其dpr是咱們設置的這個值。在此不建議手動強制設置dpr,由於在flexible中只對ios設備進行dpr的判斷,android系列的,始終認爲dpr=1
if (!dpr && !scale) { var isAndroid = win.navigator.appVersion.match(/android/gi); var isIPhone = win.navigator.appVersion.match(/iphone/gi); var devicePixelRatio = win.devicePixelRatio; if (isIPhone) { // iOS下,對於2和3的屏,用2倍的方案,其他的用1倍方案 if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) { dpr = 3; } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){ dpr = 2; } else { dpr = 1; } } else { // 其餘設備下,仍舊使用1倍的方案 dpr = 1; } scale = 1 / dpr; }
3.4 flexible的實質
flexible實質是經過js來動態改寫meta標籤,代碼相似這樣:
var metaEl = doc.createElement('meta'); var scale = isRetina ? 0.5:1; metaEl.setAttribute('name', 'viewport'); metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no'); if (docEl.firstElementChild) { document.documentElement.firstElementChild.appendChild(metaEl); } else { var wrap = doc.createElement('div'); wrap.appendChild(metaEl); documen.write(wrap.innerHTML); }
事實上它作了這樣幾件事情:
3.5 px轉化成rem
接下來就須要把視覺稿中的px轉換成rem。首先,目前平常工做中,視覺設計給到前端開發人員的視覺稿尺寸通常都是640px,750px,以及1125px寬度爲準(考慮Retina屏),如何轉換成rem呢?
這裏拿一個推廣頁作例子,視覺稿的寬度是750px,下面看看如何將其中的個元素px轉換成rem。
手淘的視覺設計師把相關的信息在視覺稿上標註出來。flexible會將視覺稿分紅100等份(爲了更好兼容vh,vw),每一份被稱爲一個單位a,同時1rem單位被認定爲10a。針對這樣的視覺稿能夠算出:
1a = 7.5px
1rem = 75px
這個例子的稿子就分紅了10a,也就是整個寬度爲10rem,<html> 對應的font-size爲75px。這樣視覺稿上的元素,只須要原始的px值除以rem基準便可。例如,此比例視覺稿中的圖片,尺寸是176px/176px,轉換爲2.346667rem/2.346667rem
3.6 如何快速計算
實際生產中,每次都要計算px轉rem,會很是麻煩。阿里開源了一些插件來解決,例如cssrem是一個css轉rem的Sublime Text3的插件。
css處理器
除了使用編輯器插件,還可使用css處理器來幫助咱們處理,好比sass,less,postcss這樣的處理器。
sass
熟悉sass的開發人員,可使用sass的函數,混合宏來實現這個功能。
sass函數大體以下:
@function px2em($px, $base-font-size: 16px) { @if (unitless($px)) { @warn "Assuming #{$px} to be in pixels, attempting to convert it into pixels for you"; @return px2em($px + 0px); // That may fail. } @else if (unit($px) == em) { @return $px; } @return ($px / $base-font-size) * 1em; }
Sass的混合宏代碼以下:
@mixin px2rem($property,$px-values,$baseline-px:16px,$support-for-ie:false){ //Conver the baseline into rems $baseline-rem: $baseline-px / 1rem * 1; //Print the first line in pixel values @if $support-for-ie { #{$property}: $px-values; } //if there is only one (numeric) value, return the property/value line for it. @if type-of($px-values) == "number" { #{$property}: $px-values / $baseline-rem; } @else { //Create an empty list that we can dump values into $rem-values:(); @each $value in $px-values{ // If the value is zero or not a number, return it @if $value == 0 or type-of($value) != "number" { $rem-values: append($rem-values, $value / $baseline-rem); } } // Return the property and its list of converted values #{$property}: $rem-values; } }
PostCss
除了Sass這樣的CSS處理器以外,還可使用px2rem工具,使用gulp任務來處理:
var gulp = require('gulp'); var postcss = require('gulp-postcss'); var px2rem = require('postcss-px2rem'); gulp.task('default', function() { var processors = [px2rem({remUnit: 75})]; return gulp.src('./src/*.css') .pipe(postcss(processors)) .pipe(gulp.dest('./dest')); });
3.7 字號不用rem
上面介紹使用rem來完成h5適配,文本又要怎麼處理呢?是否是也經過rem來處理呢?顯然,咱們在iPhone3G和iPhone4的retina屏下,但願看到的文本字號是相同的,也就是說,咱們不但願文本在Retina屏幕下變小,另外,大屏幕手機上但願看到更多的文本。如今絕大多數的字體文件都自帶一些點陣尺寸,一般是16px和24px,因此咱們不但願出現13px和15px這樣奇葩的尺寸。
如此一來,就決定了在製做h5頁面中,rem不適合用到段落文本上。在flexible整個適配方案中個,考慮文本仍是使用px做爲單位。只不過使用[data-dpr]屬性來區分不一樣的dpr下的大小:
div { width: 1rem; height: 0.4rem; font-size: 12px; // 默認寫上dpr爲1的fontSize } [data-dpr="2"] div { font-size: 24px; } [data-dpr="3"] div { font-size: 36px; }
爲了更好的利於開發,實際開發中定製一個font-dpr()這樣的sass混合宏:
@mixin font-dpr($font-size){ font-size: $font-size; [data-dpr="2"] & { font-size: $font-size * 2; } [data-dpr="3"] & { font-size: $font-size * 3; } }
有了這樣的混合宏以後,在開發中能夠直接這樣使用:
@include font-dpr(16px);
這只是針對描述性的文本,好比段落文本,有時候文本字號也是要分場景的,好比項目中有個slogan,產品但願這個slogan能根據不一樣終端適配。針對這樣的場景,徹底可使用rem給這個slogan作計量單位。
H5適配方案有不少,flexible只是其中一種。
3. 使用vm,vh實現移動端佈局
3.1 Flexible回顧
flexible解決了針對H5頁面佈局的適配問題,這是一套成熟的方案。爲了適配不一樣的終端,經過Hack手段根據設備的dpr設置viewport的值:
<!-- dpr = 1--> <meta name="viewport" content="initial-scale=scale,maximum-scale=scale,minimum-scale=scale,user-scalable=no"> <!-- dpr = 2--> <meta name="viewport" content="initial-scale=0.5,maximum-scale=0.5,minimum-scale=0.5,user-scalable=no"> <!-- dpr = 3--> <meta name="viewport" content="initial-scale=0.3333333333,maximum-scale=0.3333333333,minimum-scale=0.3333333333,user-scalable=no">
從而讓頁面達到縮放的效果,同時也實現了頁面的適配功能,主要思想有三點:
1. 根據dpr的值來修改viewport實現1px的線
2. 根據dpr的值來修改html的font-size,從而使用rem實現等比縮放
3.使用Hack手段用rem模擬vw特性
3.2 適配方案
隨着前端技術不斷變化,flexible再也不是最佳方案。移動端佈局有兩個最重要的問題:
1.各終端下的適配問題
2.Retina屏的細節處理問題
不一樣終端,面對的屏幕分辨率,DPR,1px,2px圖等一些列的問題。這個佈局方案也是針對性的解決這些問題,只不過再也不使用Hack手段來處理,而是直接使用原生的CSS技術來處理。
3.3 適配終端
首先要解決的是適配終端,flexible方案是經過javascript來模擬vm的特性,到今天爲止,vw已經獲得衆多瀏覽器的支持,也就是說,能夠直接考慮將vw用到咱們的適配佈局中。
衆所周知,vw是基於Viewport視窗的長度單位,這裏視窗(Viewport)指瀏覽器可視化區域,而這個可視化區域是window.innerWidth/window.innerHeight的大小,以下圖:
在CSS Values and Units Module Level 3這篇文章中介紹了和Viewport相關的4個單位,分別是vw,vh,vmin,vmax:
vw:是Viewport's width的簡寫,1vw表明window.innerWidth的1%
vh:和vw相似,是Viewport's height的簡寫,1vh表明window.innerHeight的1%
vmin:是當前vw和vh中的較小的值
vmax:是當前vw和vh中的較大的值
vmin和vmax是根據Viewport中長度偏大的那個惟獨值來計算出來的,若是window.innerHeight>window.innerWidth則vmin取window.innerWidth的百分之一,vmax取window.innerHeight的百分之一。以下圖:
因此在這個方案中,大膽使用vw來替換以前flexible中rem縮放方案。先來回歸到實際業務中,目前出現的視覺設計稿,大部分是使用750px寬度的,從上面的定義中能夠看出100vw = 750px,即1vw=75px,能夠根據設計圖上的px值直接轉換成對應的vw。看到這裏也須要問,每次都要計算,很麻煩,能不能簡單一點呢?其實咱們可使用PostCSS插件postcss-px-to-viewport,讓咱們能夠在代碼中直接使用px,以下:
[w-369]{ width: 369px; } [w-369] h2 span { background: #FF5000; color: #fff; display: inline-block; border-radius: 4px; font-size: 20px; text-shadow: 0 2px 2px #FF5000; padding: 2px 5px; margin-right: 5px; }
通過PostCSS編譯以後就是咱們所須要的vw代碼:
[w-369] { width: 49.2vw; } [w-369] h2 span { background: #ff5000; color: #fff; display: inline-block; border-radius: .53333vw; text-shadow: 0 0.26667vw 0.26667vw #ff5000; padding: .26667vw .66667vw; } [w-369] h2 span, [w-369] i { font-size: 2.66667vw; margin-right: .66667vw; }
實際使用的時候,能夠對插件進行相關的參數配置,以下:
"postcss-px-to-viewport": { viewportWidth: 750, viewportHeight: 1334, unitPrecision: 5, viewportUnit: 'vw', selectorBlackList: [], minPixelValue: 1, mediaQuery: false }
假設你的設計稿不是750px而是1125px,那麼就能夠修改viewportWidth的值爲1125px。更多的介紹能夠參考使用文檔。
3.4 使用場合
上面解決了px到vw的轉換計算,那麼在那些地方可使用vm來適配咱們的頁面呢,根據相關的測試:
1. 容器適配,可使用vw
2. 文本適配,可使用vw
3. 大於1px的邊框,圓角,陰影均可以使用vw
4. 內邊距和外邊距可使用vw
長寬比
另外還有一個細節須要提出,例如咱們有一個這樣的設計:
若是直接使用:
[w-188-246] { width: 188px; } [w-187-246]{ width: 187px }
最終效果會形成[w-187-246]容器的高度小於[w-188-246]。這個時候就要考慮到容器的長寬比縮放。這方面的方案不少,但咱們仍是推薦使用工具來處理。這裏推薦一個PostCSS插件。這個插件的使用很簡單,不須要作任何配置,只須要本地安裝一下就行了,使用以下:
[aspectratio] { position: relative; } [aspectratio]::before { content: ''; display: block; width: 1px; margin-left: -1px; height: 0; } [aspectratio-content] { position: absolute; top: 0; left: 0; right: 0; bottom: 0; width: 100%; height: 100%; } [aspectratio][aspect-ratio="188/246"]{ aspect-ratio: '188:246'; }
翻譯出來:
[aspectratio][aspect-ratio="188/246"]:before { padding-top: 130.85106382978725%;
這樣就能夠實現長寬比的效果。
目前採用PostCSS插件只是一個過渡階段,在未來能夠直接在CSS中使用aspect-ratio屬性來實現長寬比。
解決1px方案
前面提到過,對於1px不建議直接轉換成vw單位,在Retina下,咱們時鐘須要面對如何解決1px的問題。這裏推薦使用一種解決1px的方案,依舊是使用PossCSS插件postcss-write-svg
使用postcss-write-svg能夠經過border-img或者background-image兩種方式來處理,好比:
@svg 1px-border { height: 2px; @rect { fill: var(--color, black); width: 100%; height: 50%; } } .example { border: 1px solid transparent; border-image: svg(1px-border param(--color #00b1ff)) 2 2 stretch; }
這樣PostCSS自動幫你翻譯過來:
.example { border: 1px solid transparent; border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='2px'%3E%3Crect fill='%2300b1ff' width='100%25' height='50%25'/%3E%3C/svg%3E") 2 2 stretch; }
使用PostCSS插件比咱們修改圖片要來得簡單與方便。
上面延時的是使用border-image方式,除此以外,還可使用background-image來實現。以下:
@svg square { @rect { fill: var(--color, black); width: 100%; height: 100%; } } #example { background: white svg(square param(--color #00b1ff)); }
編譯出來就是:
#example { background: white url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Crect fill='%2300b1ff' width='100%25' height='100%25'/%3E%3C/svg%3E"); }
這個方案簡單易用,是咱們所須要的,也基本能達到全部需求,可是必定要記得在<head>標籤中添加下面這一句:
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no" />
3.5 總結一下
這個適配方案中所用到的技術點,簡單總結一下:
使用vw來實現頁面的適配,而且經過PostCSS的插件postcss-px-to-viewport把px轉換成vw,這樣咱們在寫代碼不須要作任何的計算,只須要按照原型設計稿中的尺寸來寫代碼,可是要配置一下設計稿的尺寸。
爲了更好的實現長寬比,特別是針對img,vedio和iframe元素,經過postcss-aspect-ratio-mini來實現,在實際使用中,只須要吧對應的寬和高寫進去便可。
爲了解決1px問題,使用PostCSS插件postcss-write-svg自動生成border-image或者background-img的圖片。
這裏使用了多個PostCSS插件,其實不少優秀的PostCSS插件能幫助咱們解決不少問題,能夠在這裏關於PostCSS相關的文章查閱相關的文章。若是要系統的學些PostCSS,能夠購買《深刻PostCSS Web設計》這本書。
3.6 降級處理
本節最開始提到,目前爲止T30的機型中還有幾款機型不支持vw的適配方案。若是業務須要,應該怎麼處理呢?有兩種方式能夠進行降級處理:
CSS Houdini:經過CSS Houdini針對vw作處理,調用CSS Typed OM Level1提供的CSSUnitValue API.
CSS Polyfill:經過對應的Polyfill作相應的處理,目前針對vw單位的Polyfill主要有:vminpoly,Viewport Units Buggyfill,vunits.js和Modernizr,推薦使用Viewport Units Buggyfill
3.7 Viewport不足之處
採用vw作適配並非沒有任何缺陷,有一些細節仍是存在不足之處的。好比容器使用vw單位,margin採用px單位,很容易形成總體寬度超過100vw,從而印象佈局效果。對於這種狀況咱們可使用相關的技術進行避免。好比將margin換成padding,而且配合box-sizing。只不過這不是最佳方案,隨着瀏覽器或者應用自身的Viewport對calc()函數支持之後,碰到vw和px混合使用的時候,能夠結合calc()函數一塊兒使用,這樣能夠完美解決。
另外,px轉換成vw單位,多少仍是存在一些像素差,畢竟不少時候不能徹底整除。
3.8 如何判斷本身的應用是否支持
雖然不少瀏覽器都支持vw,在使用的時候仍是會擔憂本身的app應用是否支持vw,能夠打開下面的頁面查看
頁面測試完成後,找到對應的Valus and units列表項:
若是vw欄是綠色的表明你的設備支持,反之不支持。另外你能夠常常關注css3test相關的更新,後面會根據相關的規範更新測試代碼,讓你快速掌握那些屬性能夠大膽使用。
參考:
https://www.w3cplus.com/css/towards-retina-web.htmlhttps://www.w3cplus.com/css/viewports.htmlhttps://www.quirksmode.org/mobile/viewports.htmlhttps://www.zhangxinxu.com/wordpress/2012/10/new-pad-retina-devicepixelratio-css-page/