移動前端開發之viewport的深入理解

來源:互聯網
上載者:User

標籤:col   總結   viewport   效果   意義   mobile   20px   one   attr   

在行動裝置上進行網頁的重構或開發,首先得搞明白的就是行動裝置上的viewport了,只有明白了viewport的概念以及弄清楚了跟viewport有關的meta標籤的使用,才能更好地讓我們的網頁適配或響應各種不同解析度的行動裝置。

一、viewport的概念

通俗的講,行動裝置上的viewport就是裝置的螢幕上能用來顯示我們的網頁的那一塊地區,在具體一點,就是瀏覽器上(也可能是一個app中的webview)用來顯示網頁的那部分地區,但viewport又不局限於瀏覽器可視地區的大小,它可能比瀏覽器的可視地區要大,也可能比瀏覽器的可視地區要小。在預設情況下,一般來講,行動裝置上的viewport都是要大於瀏覽器可視地區的,這是因為考慮到行動裝置的解析度相對於案頭電腦來說都比較小,所以為了能在行動裝置上正常顯示那些傳統的為案頭瀏覽器設計的網站,行動裝置上的瀏覽器都會把自己預設的viewport設為980px或1024px(也可能是其它值,這個是由裝置自己決定的),但帶來的後果就是瀏覽器會出現橫向捲軸,因為瀏覽器可視地區的寬度是比這個預設的viewport的寬度要小的。列出了一些裝置上瀏覽器的預設viewport的寬度。

 

二、css中的1px並不等於裝置的1px

    在css中我們一般使用px作為單位,在案頭瀏覽器中css的1個像素往往都是對應著電腦螢幕的1個物理像素,這可能會造成我們的一個錯覺,那就是css中的像素就是裝置的物理像素。但實際情況卻並非如此,css中的像素只是一個抽象的單位,在不同的裝置或不同的環境中,css中的1px所代表的裝置物理像素是不同的。在為案頭瀏覽器設計的網頁中,我們無需對這個津津計較,但在行動裝置上,必須弄明白這點。在早先的行動裝置中,螢幕像素密度都比較低,如iphone3,它的解析度為320x480,在iphone3上,一個css像素確實是等於一個螢幕物理像素的。後來隨著技術的發展,行動裝置的螢幕像素密度越來越高,從iphone4開始,蘋果公司便推出了所謂的Retina屏,解析度提高了一倍,變成640x960,但螢幕尺寸卻沒變化,這就意味著同樣大小的螢幕上,像素卻多了一倍,這時,一個css像素是等於兩個物理像素的。其他品牌的行動裝置也是這個道理。例如安卓裝置根據螢幕像素密度可分為ldpi、mdpi、hdpi、xhdpi等不同的等級,解析度也是五花八門,安卓裝置上的一個css像素相當於多少個螢幕物理像素,也因裝置的不同而不同,沒有一個定論。

    還有一個因素也會引起css中px的變化,那就是使用者縮放。例如,當使用者把頁面放大一倍,那麼css中1px所代表的物理像素也會增加一倍;反之把頁面縮小一倍,css中1px所代表的物理像素也會減少一倍。關於這點,在文章後面的部分還會講到。

    在移動端瀏覽器中以及某些案頭瀏覽器中,window對象有一個devicePixelRatio屬性,它的官方的定義為:裝置物理像素和裝置獨立像素的比例,也就是 devicePixelRatio = 物理像素 / 獨立像素。css中的px就可以看做是裝置的獨立像素,所以通過devicePixelRatio,我們可以知道該裝置上一個css像素代表多少個物理像素。例如,在Retina屏的iphone上,devicePixelRatio的值為2,也就是說1個css像素相當於2個物理像素。但是要注意的是,devicePixelRatio在不同的瀏覽器中還存在些許的相容性問題,所以我們現在還並不能完全信賴這個東西,具體的情況可以看下這篇文章。

devicePixelRatio的測試結果:

 

三、PPK的關於三個viewport的理論

    ppk大神對於行動裝置上的viewport有著非常多的研究(第一篇,第二篇,第三篇),有興趣的同學可以去看一下,本文中有很多資料和觀點也是出自那裡。ppk認為,行動裝置上有三個viewport。

    首先,行動裝置上的瀏覽器認為自己必須能讓所有的網站都正常顯示,即使是那些不是為行動裝置設計的網站。但如果以瀏覽器的可視地區作為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 而設計的網站,不需要使用者手動縮放,也不需要出現橫向捲軸,都可以完美的呈現給使用者。

 

四、利用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  這個屬性了,所以這個屬性我們要避免進行使用  。

 

五、把當前的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">

 

六、關於meta viewport的更多知識

1、關於縮放以及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的寬度就是螢幕的寬度)的目的。

         

 

2、動態改變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標籤進行覆蓋或更改,會出現讓人非常迷糊的結果。

 

七、結語

說了那麼多廢話,最後還是有必要總結一點有用的出來。

首先如果不設定meta viewport標籤,那麼行動裝置上瀏覽器預設的寬度值為800px,980px,1024px等這些,總之是大於螢幕寬度的。這裡的寬度所用的單位px都是指css中的px,它跟代表實際螢幕物理像素的px不是一回事。

第二、每個行動裝置瀏覽器中都有一個理想的寬度,這個理想的寬度是指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、384、400等幾種,都是非常接近的,理想寬度的相近也就意味著我們針對某個裝置的理想viewport而做出的網站,在其他裝置上的表現也不會相差非常多甚至是表現一樣的。

移動前端開發之viewport的深入理解

相關文章

Alibaba Cloud 10 Year Anniversary

With You, We are Shaping a Digital World, 2009-2019

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。