在移動設備上進行網頁的重構或開發,首先得搞明白的就是移動設備上的viewport了,只有明白了viewport的概念以及弄清楚了跟viewport有關的meta標籤的使用,才能更好地讓咱們的網頁適配或響應各類不一樣分辨率的移動設備。javascript
1、viewport的概念css
通俗的講,移動設備上的viewport就是設備的屏幕上能用來顯示咱們的網頁的那一塊區域,在具體一點,就是瀏覽器上(也多是一個app中的webview)用來顯示網頁的那部分區域,但viewport又不侷限於瀏覽器可視區域的大小,它可能比瀏覽器的可視區域要大,也可能比瀏覽器的可視區域要小。在默認狀況下,通常來說,移動設備上的viewport都是要大於瀏覽器可視區域的,這是由於考慮到移動設備的分辨率相對於桌面電腦來講都比較小,因此爲了能在移動設備上正常顯示那些傳統的爲桌面瀏覽器設計的網站,移動設備上的瀏覽器都會把本身默認的viewport設爲980px或1024px(也多是其它值,這個是由設備本身決定的),但帶來的後果就是瀏覽器會出現橫向滾動條,由於瀏覽器可視區域的寬度是比這個默認的viewport的寬度要小的。下圖列出了一些設備上瀏覽器的默認viewport的寬度。html
2、css中的1px並不等於設備的1pxjava
在css中咱們通常使用px做爲單位,在桌面瀏覽器中css的1個像素每每都是對應着電腦屏幕的1個物理像素,這可能會形成咱們的一個錯覺,那就是css中的像素就是設備的物理像素。但實際狀況卻並不是如此,css中的像素只是一個抽象的單位,在不一樣的設備或不一樣的環境中,css中的1px所表明的設備物理像素是不一樣的。在爲桌面瀏覽器設計的網頁中,咱們無需對這個津津計較,但在移動設備上,必須弄明白這點。在早先的移動設備中,屏幕像素密度都比較低,如iphone3,它的分辨率爲320x480,在iphone3上,一個css像素確實是等於一個屏幕物理像素的。後來隨着技術的發展,移動設備的屏幕像素密度愈來愈高,從iphone4開始,蘋果公司便推出了所謂的Retina屏,分辨率提升了一倍,變成640x960,但屏幕尺寸卻沒變化,這就意味着一樣大小的屏幕上,像素卻多了一倍,這時,一個css像素是等於兩個物理像素的。其餘品牌的移動設備也是這個道理。例如安卓設備根據屏幕像素密度可分爲ldpi、mdpi、hdpi、xhdpi等不一樣的等級,分辨率也是五花八門,安卓設備上的一個css像素至關於多少個屏幕物理像素,也因設備的不一樣而不一樣,沒有一個定論。web
還有一個因素也會引發css中px的變化,那就是用戶縮放。例如,當用戶把頁面放大一倍,那麼css中1px所表明的物理像素也會增長一倍;反之把頁面縮小一倍,css中1px所表明的物理像素也會減小一倍。關於這點,在文章後面的部分還會講到。windows
在移動端瀏覽器中以及某些桌面瀏覽器中,window對象有一個devicePixelRatio屬性,它的官方的定義爲:設備物理像素和設備獨立像素的比例,也就是 devicePixelRatio = 物理像素 / 獨立像素。css中的px就能夠看作是設備的獨立像素,因此經過devicePixelRatio,咱們能夠知道該設備上一個css像素表明多少個物理像素。例如,在Retina屏的iphone上,devicePixelRatio的值爲2,也就是說1個css像素至關於2個物理像素。可是要注意的是,devicePixelRatio在不一樣的瀏覽器中還存在些許的兼容性問題,因此咱們如今還並不能徹底信賴這個東西,具體的狀況能夠看下這篇文章。瀏覽器
devicePixelRatio的測試結果:app
3、PPK的關於三個viewport的理論iphone
ppk大神對於移動設備上的viewport有着很是多的研究(第一篇,第二篇,第三篇),有興趣的同窗能夠去看一下,本文中有不少數據和觀點也是出自那裏。ppk認爲,移動設備上有三個viewport。ide
首先,移動設備上的瀏覽器認爲本身必須能讓全部的網站都正常顯示,即便是那些不是爲移動設備設計的網站。但若是以瀏覽器的可視區域做爲viewport的話,由於移動設備的屏幕都不是很寬,因此那些爲桌面瀏覽器設計的網站放到移動設備上顯示時,必然會由於移動設備的viewport太窄,而擠做一團,甚至佈局什麼的都會亂掉。也許有人會問,如今不是有不少手機分辨率都很是大嗎,好比768x1024,或者1080x1920這樣,那這樣的手機用來顯示爲桌面瀏覽器設計的網站是沒問題的吧?前面咱們已經說了,css中的1px並非表明屏幕上的1px,你分辨率越大,css中1px表明的物理像素就會越多,devicePixelRatio的值也越大,這很好理解,由於你分辨率增大了,但屏幕尺寸並無變大多少,必須讓css中的1px表明更多的物理像素,才能讓1px的東西在屏幕上的大小與那些低分辨率的設備差很少,否則就會由於過小而看不清。因此在1080x1920這樣的設備上,在默認狀況下,也許你只要把一個div的寬度設爲300多px(視devicePixelRatio的值而定),就是滿屏的寬度了。回到正題上來,若是把移動設備上瀏覽器的可視區域設爲viewport的話,某些網站就會由於viewport太窄而顯示錯亂,因此這些瀏覽器就決定默認狀況下把viewport設爲一個較寬的值,好比980px,這樣的話即便是那些爲桌面設計的網站也能在移動瀏覽器上正常顯示了。ppk把這個瀏覽器默認的viewport叫作 layout viewport。這個layout viewport的寬度能夠經過 document.documentElement.clientWidth 來獲取。
然而,layout viewport 的寬度是大於瀏覽器可視區域的寬度的,因此咱們還須要一個viewport來表明 瀏覽器可視區域的大小,ppk把這個viewport叫作 visual viewport。visual viewport的寬度能夠經過window.innerWidth 來獲取,但在Android 2, Oprea mini 和 UC 8中沒法正確獲取。
如今咱們已經有兩個viewport了:layout viewport 和 visual viewport。但瀏覽器以爲還不夠,由於如今愈來愈多的網站都會爲移動設備進行單獨的設計,因此必須還要有一個能完美適配移動設備的viewport。所謂的完美適配指的是,首先不須要用戶縮放和橫向滾動條就能正常的查看網站的全部內容;第二,顯示的文字的大小是合適,好比一段14px大小的文字,不會由於在一個高密度像素的屏幕裏顯示得過小而沒法看清,理想的狀況是這段14px的文字不管是在何種密度屏幕,何種分辨率下,顯示出來的大小都是差很少的。固然,不僅是文字,其餘元素像圖片什麼的也是這個道理。ppk把這個viewport叫作 ideal viewport,也就是第三個viewport——移動設備的理想viewport。
ideal viewport並無一個固定的尺寸,不一樣的設備擁有有不一樣的ideal viewport。全部的iphone的ideal viewport寬度都是320px,不管它的屏幕寬度是320仍是640,也就是說,在iphone中,css中的320px就表明iphone屏幕的寬度。
可是安卓設備就比較複雜了,有320px的,有360px的,有384px的等等,關於不一樣的設備ideal viewport的寬度都爲多少,能夠到http://viewportsizes.com去查看一下,裏面收集了衆多設備的理想寬度。
再總結一下:ppk把移動設備上的viewport分爲layout viewport 、 visual viewport 和 ideal viewport 三類,其中的ideal viewport是最適合移動設備的viewport,ideal viewport的寬度等於移動設備的屏幕寬度,只要在css中把某一元素的寬度設爲ideal viewport的寬度(單位用px),那麼這個元素的寬度就是設備屏幕的寬度了,也就是寬度爲100%的效果。ideal viewport 的意義在於,不管在何種分辨率的屏幕下,那些針對ideal viewport 而設計的網站,不須要用戶手動縮放,也不須要出現橫向滾動條,均可以完美的呈現給用戶。
4、利用meta標籤對viewport進行控制
移動設備默認的viewport是layout viewport,也就是那個比屏幕要寬的viewport,但在進行移動設備網站的開發時,咱們須要的是ideal viewport。那麼怎麼才能獲得ideal viewport呢?這就該輪到meta標籤出場了。
咱們在開發移動設備的網站時,最多見的的一個動做就是把下面這個東西複製到咱們的head標籤中:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
該meta標籤的做用是讓當前viewport的寬度等於設備的寬度,同時不容許用戶手動縮放。也許允不容許用戶縮放不一樣的網站有不一樣的要求,但讓viewport的寬度等於設備的寬度,這個應該是你們都想要的效果,若是你不這樣的設定的話,那就會使用那個比屏幕寬的默認viewport,也就是說會出現橫向滾動條。
這個name爲viewport的meta標籤到底有哪些東西呢,又都有什麼做用呢?
meta viewport 標籤首先是由蘋果公司在其safari瀏覽器中引入的,目的就是解決移動設備的viewport問題。後來安卓以及各大瀏覽器廠商也都紛紛效仿,引入對meta viewport的支持,事實也證實這個東西仍是很是有用的。
在蘋果的規範中,meta viewport 有6個屬性(暫且把content中的那些東西稱爲一個個屬性和值),以下:
width | 設置layout viewport 的寬度,爲一個正整數,或字符串"width-device" |
initial-scale | 設置頁面的初始縮放值,爲一個數字,能夠帶小數 |
minimum-scale | 容許用戶的最小縮放值,爲一個數字,能夠帶小數 |
maximum-scale | 容許用戶的最大縮放值,爲一個數字,能夠帶小數 |
height | 設置layout viewport 的高度,這個屬性對咱們並不重要,不多使用 |
user-scalable | 是否容許用戶進行縮放,值爲"no"或"yes", no 表明不容許,yes表明容許 |
這些屬性能夠同時使用,也能夠單獨使用或混合使用,多個屬性同時使用時用逗號隔開就好了。
此外,在安卓中還支持 target-densitydpi 這個私有屬性,它表示目標設備的密度等級,做用是決定css中的1px表明多少物理像素
target-densitydpi | 值能夠爲一個數值或 high-dpi 、 medium-dpi、 low-dpi、 device-dpi 這幾個字符串中的一個 |
特別說明的是,當 target-densitydpi=device-dpi 時, css中的1px會等於物理像素中的1px。
由於這個屬性只有安卓支持,而且安卓已經決定要廢棄target-densitydpi 這個屬性了,因此這個屬性咱們要避免進行使用 。
5、把當前的viewport寬度設置爲 ideal viewport 的寬度
要獲得ideal viewport就必須把默認的layout viewport的寬度設爲移動設備的屏幕寬度。由於meta viewport中的width能控制layout viewport的寬度,因此咱們只須要把width設爲width-device這個特殊的值就好了。
<meta name="viewport" content="width=device-width">
下圖是這句代碼在各大移動端瀏覽器上的測試結果:
能夠看到經過width=device-width,全部瀏覽器都能把當前的viewport寬度變成ideal viewport的寬度,但要注意的是,在iphone和ipad上,不管是豎屏仍是橫屏,寬度都是豎屏時ideal viewport的寬度。
這樣的寫法看起來誰都會作,沒吃過豬肉,誰還沒見過豬跑啊~,確實,咱們在開發移動設備上的網頁時,無論你明不明白什麼是viewport,可能你只須要這麼一句代碼就夠了。
但是你確定不知道
<meta name="viewport" content="initial-scale=1">
這句代碼也能達到和前一句代碼同樣的效果,也能夠把當前的的viewport變爲 ideal viewport。
呵呵,傻眼了吧,由於從理論上來說,這句代碼的做用只是不對當前的頁面進行縮放,也就是頁面本該是多大就是多大。那爲何會有 width=device-width 的效果呢?
要想清楚這件事情,首先你得弄明白這個縮放是相對於什麼來縮放的,由於這裏的縮放值是1,也就是沒縮放,但卻達到了 ideal viewport 的效果,因此,那答案就只有一個了,縮放是相對於 ideal viewport來進行縮放的,當對ideal viewport進行100%的縮放,也就是縮放值爲1的時候,不就獲得了 ideal viewport嗎?事實證實,的確是這樣的。下圖是各大移動端的瀏覽器當設置了<meta name="viewport" content="initial-scale=1"> 後是否能把當前的viewport寬度變成 ideal viewport 的寬度的測試結果。
測試結果代表 initial-scale=1 也能把當前的viewport寬度變成 ideal viewport 的寬度,但此次輪到了windows phone 上的IE 不管是豎屏仍是橫屏都把寬度設爲豎屏時ideal viewport的寬度。但這點小瑕疵已經可有可無了。
但若是width 和 initial-scale=1同時出現,而且還出現了衝突呢?好比:
<meta name="viewport" content="width=400, initial-scale=1">
width=400表示把當前viewport的寬度設爲400px,initial-scale=1則表示把當前viewport的寬度設爲ideal viewport的寬度,那麼瀏覽器到底該服從哪一個命令呢?是書寫順序在後面的那個嗎?不是。當遇到這種狀況時,瀏覽器會取它們兩個中較大的那個值。例如,當width=400,ideal viewport的寬度爲320時,取的是400;當width=400, ideal viewport的寬度爲480時,取的是ideal viewport的寬度。(ps:在uc9瀏覽器中,當initial-scale=1時,不管width屬性的值爲多少,此時viewport的寬度永遠都是ideal viewport的寬度)
最後,總結一下,要把當前的viewport寬度設爲ideal viewport的寬度,既能夠設置 width=device-width,也能夠設置 initial-scale=1,但這二者各有一個小缺陷,就是iphone、ipad以及IE 會橫豎屏不分,統統以豎屏的ideal viewport寬度爲準。因此,最完美的寫法應該是,二者都寫上去,這樣就 initial-scale=1 解決了 iphone、ipad的毛病,width=device-width則解決了IE的毛病:
<meta name="viewport" content="width=device-width, initial-scale=1">
6、關於meta viewport的更多知識
一、關於縮放以及initial-scale的默認值
首先咱們先來討論一下縮放的問題,前面已經提到過,縮放是相對於ideal viewport來縮放的,縮放值越大,當前viewport的寬度就會越小,反之亦然。例如在iphone中,ideal viewport的寬度是320px,若是咱們設置 initial-scale=2 ,此時viewport的寬度會變爲只有160px了,這也好理解,放大了一倍嘛,就是原來1px的東西變成2px了,可是1px變爲2px並非把原來的320px變爲640px了,而是在實際寬度不變的狀況下,1px變得跟原來的2px的長度同樣了,因此放大2倍後原來須要320px才能填滿的寬度如今只須要160px就作到了。所以,咱們能夠得出一個公式:
visual viewport寬度 = ideal viewport寬度 / 當前縮放值 當前縮放值 = ideal viewport寬度 / visual viewport寬度
ps: visual viewport的寬度指的是瀏覽器可視區域的寬度。
大多數瀏覽器都符合這個理論,可是安卓上的原生瀏覽器以及IE有些問題。安卓自帶的webkit瀏覽器只有在 initial-scale = 1 以及沒有設置width屬性時纔是表現正常的,也就至關於這理論在它身上基本沒用;而IE則根本不甩initial-scale這個屬性,不管你給他設置什麼,initial-scale表現出來的效果永遠是1。
好了,如今再來講下initial-scale的默認值問題,就是不寫這個屬性的時候,它的默認值會是多少呢?很顯然不會是1,由於當 initial-scale = 1 時,當前的layout viewport寬度會被設爲 ideal viewport的寬度,但前面說了,各瀏覽器默認的 layout viewport寬度通常都是980啊,1024啊,800啊等等這些個值,沒有一開始就是 ideal viewport的寬度的,因此 initial-scale的默認值確定不是1。安卓設備上的initial-scale默認值好像沒有方法可以獲得,或者就是乾脆它就沒有默認值,必定要你顯示的寫出來這個東西纔會起做用,咱們無論它了,這裏咱們重點說一下iphone和ipad上的initial-scale默認值。
根據測試,咱們能夠在iphone和ipad上獲得一個結論,就是不管你給layout viewpor設置的寬度是多少,而又沒有指定初始的縮放值的話,那麼iphone和ipad會自動計算initial-scale這個值,以保證當前layout viewport的寬度在縮放後就是瀏覽器可視區域的寬度,也就是說不會出現橫向滾動條。好比說,在iphone上,咱們不設置任何的viewport meta標籤,此時layout viewport的寬度爲980px,但咱們能夠看到瀏覽器並無出現橫向滾動條,瀏覽器默認的把頁面縮小了。根據上面的公式,當前縮放值 = ideal viewport寬度 / visual viewport寬度,咱們能夠得出:
當前縮放值 = 320 / 980
也就是當前的initial-scale默認值應該是 0.33這樣子。當你指定了initial-scale的值後,這個默認值就不起做用了。
總之記住這個結論就好了:在iphone和ipad上,不管你給viewport設的寬的是多少,若是沒有指定默認的縮放值,則iphone和ipad會自動計算這個縮放值,以達到當前頁面不會出現橫向滾動條(或者說viewport的寬度就是屏幕的寬度)的目的。
二、動態改變meta viewport標籤
第一種方法
可使用document.write來動態輸出meta viewport標籤,例如:
document.write('<meta name="viewport" content="width=device-width,initial-scale=1">')
第二種方法
經過setAttribute來改變
<meta id="testViewport" name="viewport" content="width = 380"> <script> var mvp = document.getElementById('testViewport'); mvp.setAttribute('content','width=480'); </script>
安卓2.3自帶瀏覽器上的一個bug
<meta name="viewport" content="width=device-width"> <script type="text/javascript"> alert(document.documentElement.clientWidth); //彈出600,正常狀況應該彈出320 </script> <meta name="viewport" content="width=600"> <script type="text/javascript"> alert(document.documentElement.clientWidth); //彈出320,正常狀況應該彈出600 </script>
測試的手機ideal viewport 寬度爲320px,第一次彈出的值是600,但這個值應該是第行meta標籤的結果啊,而後第二次彈出的值是320,這纔是第一行meta標籤所達到的效果啊,因此在安卓2.3(或許是全部2.x版本中)的自帶瀏覽器中,對meta viewport標籤進行覆蓋或更改,會出現讓人很是迷糊的結果。
7、結語
說了那麼多廢話,最後仍是有必要總結一點有用的出來。
首先若是不設置meta viewport標籤,那麼移動設備上瀏覽器默認的寬度值爲800px,980px,1024px等這些,總之是大於屏幕寬度的。這裏的寬度所用的單位px都是指css中的px,它跟表明實際屏幕物理像素的px不是一回事。
第2、每一個移動設備瀏覽器中都有一個理想的寬度,這個理想的寬度是指css中的寬度,跟設備的物理寬度沒有關係,在css中,這個寬度就至關於100%的所表明的那個寬度。咱們能夠用meta標籤把viewport的寬度設爲那個理想的寬度,若是不知道這個設備的理想寬度是多少,那麼用device-width這個特殊值就好了,同時initial-scale=1也有把viewport的寬度設爲理想寬度的做用。因此,咱們可使用
<meta name="viewport" content="width=device-width, initial-scale=1">
來獲得一個理想的viewport(也就是前面說的ideal viewport)。
爲何須要有理想的viewport呢?好比一個分辨率爲320x480的手機理想viewport的寬度是320px,而另外一個屏幕尺寸相同但分辨率爲640x960的手機的理想viewport寬度也是爲320px,那爲何分辨率大的這個手機的理想寬度要跟分辨率小的那個手機的理想寬度同樣呢?這是由於,只有這樣才能保證一樣的網站在不一樣分辨率的設備上看起來都是同樣或差很少的。實際上,如今市面上雖然有那麼多不一樣種類不一樣品牌不一樣分辨率的手機,但它們的理想viewport寬度概括起來無非也就 320、360、38四、400等幾種,都是很是接近的,理想寬度的相近也就意味着咱們針對某個設備的理想viewport而作出的網站,在其餘設備上的表現也不會相差很是多甚至是表現同樣的。