深入淺出viewport

開發移動網頁時,你一定會遇到下面這段代碼:

<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=2.0, user-scalable=yes" />

在網頁的<head>中增加以上這句話,可以讓網頁的寬度自動適應手機屏幕的寬度。其中:

width=device-width :表示寬度是設備屏幕的寬度
initial-scale=1.0:表示初始的縮放比例
minimum-scale=0.5:表示最小的縮放比例
maximum-scale=2.0:表示最大的縮放比例
user-scalable=yes:表示用戶是否可以調整縮放比例

懂了嗎?沒懂?!好,繼續往下看,保證你懂。

設備像素和CSS像素

你需要明白的第一個概念是CSS像素,以及它和設備像素的區別。

設備像素是我們直覺上覺得「靠譜」的像素。這些像素為你所使用的各種設備都提供了正規的分辨率,并且其值可以(通常情況下)從screen.width/height屬性中讀出。

如果你給一個元素設置了width: 128px的屬性,并且你的顯示器是1024px寬,當你最大化你的瀏覽器屏幕,這個元素將會在你的顯示器上重復顯示8次(大概是這樣;我們先忽略那些微妙的地方)。

如果用戶進行縮放,那么計算方式將會發生變化。如果用戶放大到200%,那么你的那個擁有width: 128px屬性的元素在1024px寬的顯示器上只會重復顯示4次。

現代瀏覽器中實現縮放的方式無怪乎都是「拉伸」像素。所以,元素的寬度并沒有從128個像素被修改為256個像素;相反是實際像素被放大了兩倍。形式上,元素仍然是128個CSS像素寬,即使它占據了256個設備像素的空間。

換句話說,放大到200%使一個CSS像素變成為一個設備像素的四倍。(寬度2倍,高度2倍,總共4倍)

一些配圖可以解釋清楚這個概念。這兒有四個100%縮放比的元素。這兒沒有什么值得看的;CSS像素與設備像素完全重疊。

1

現在讓我們縮小。CSS像素開始收縮,這意味著現在一個設備像素覆蓋了多個CSS像素。

2

如果你進行放大,相反的行為會發生。CSS像素開始變大,現在一個CSS像素覆蓋了多個設備像素。

3

這兒的要點是你只對CSS像素感興趣。這些就是那些控制你的樣式表如何被渲染的像素。

設備像素對你(譯者:指的是開發者)來說基本上沒用。但是對于用戶不一樣;用戶將會放大或者縮小頁面直到他能舒服的閱讀為止。無論怎樣,縮放比例對你不會產生影響。瀏覽器將會自動的使你的CSS布局被拉伸或者被壓縮。

100%縮放

我是以假設縮放比例為100%來開始這個例子的。是時候需要更加嚴格的來定義一下這個100%了:

在縮放比例100%的情況下一個CSS像素完全等于一個設備像素。

100%縮放的概念在接下來的解釋中會非常有用,但是在你的日常工作中你不用過分的擔心它。在桌面環境上你將會在100%縮放比例的情況下測試你的站點,但即使用戶放大或者縮小,CSS像素的魔力將會保證你的布局保持相同的比率。

為桌面設計的網頁在手機上如何顯示?

先說一下PC web的兩種布局,一般的網頁如果用固定布局都會定義好整個頁面的寬度,常見的寬度是980px,當屏幕分辨率的寬度大于980px的時候,如:1024768,頁面就居中,兩邊留白;如果屏幕分辨率小于980px的時候,如:800600,頁面就會出現橫向的滾動條,這應該是所有前端開發人員都不希望出現的,所幸的是目前大多數顯示器的屏幕分辨率都是1024*768以上的,所以寬度為980px的固定布局是安全又放心的。而如果用流動布局做網頁的話一般要自應適不同的分辨率滿屏顯示以讓內容區域達到最大化,流動布局的例子有很多,如郵箱、博客園等等。

在顯示面積上手機屏幕相對桌面顯示器要小很多,在幾年前(現在也如此)大部分網站都是為桌面顯示器瀏覽而設計,很少考慮到適應手機屏幕,所以如果用手機瀏覽大多網站時會出現問題,比如常見固定寬度的網頁會出現橫向豎向滑動條,當然這不算什么大問題;但如果是瀏覽流動布局的網頁那情況會非常糟糕,設想一個寬度為 30% 的側邊欄對于 320px 手機屏幕而言也就 96px,只能容納八個 12px 的漢字,可閱讀性非常差。

接下來說說手機上打開一個PC web頁面,用手機打開一個寬度為980的固定布局頁面,頁面會默認縮放到剛好滿屏顯示,并不會出現橫向滾動條,這個現象并不讓我感到奇怪,我認為這是手機廠商的一些設定造成的,但關鍵是做了什么“手腳”,后來網上查閱了很多資料知道這是因為瀏覽器的兩個viewport:layout viewport & visual viewport

手機瀏覽器在顯示網頁時,會在設備屏幕(設備分辨率)上創建一個980px(css像素)的虛擬窗口,然后使用該虛擬窗口顯示網頁,所以網站會縮小顯示。一般把這個虛擬窗口稱為layout viewport。

說白了其實就是把980px的CSS像素裝進了320px(假設手機分辨率是320px)的設備像素下。

layout viewport的默認值

在Apple實現viewport后,其他瀏覽器也加入了對viewport meta的支持,但彼此間還是有些差異,差異最大的是layout viewport的表現:

Safari iPhone: 980px
Opera: 850px
Android WebKit: 800px
IE: 974px

為什么要使用viewport?

有了 layout viewport 似乎解決手機瀏覽網頁的難題,但如果遇到專門為手機優化的網頁就又有新的問題:

4

是的,因為 iPhone 的 layout viewport 默認為 980px,導致專為其優化的 320px 寬的頁面只能以縮放的方式顯示,這時就需要對 viewport 進行設置:

<head>
...
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/>
...
</head>

這個是最常見的一條 viewport meta 代碼,將 viewport 定義為:寬度為設備寬度,初始縮放比例為 1 倍,禁止用戶縮放。設置好后我們的頁面就顯示完美了:

4

一般的桌面端網頁都不會添加 viewport 設置,用智能手機查看這些頁面時,是在layout viewport下顯示的。

width=device-width

因為大多數情況下,<meta name="viewport" content="width=device-width" /> 這個標簽對我們來說是最給力的,可以讓我們的頁面里的圖文顯示的是正常的,很大程度上提高了頁面的可讀性。而這一meta標簽的功能就是設置layout viewport為device-width的寬度。但是device-width具體是什么呢?

第一代iphone的時候,分辨率為320480,屏幕尺寸為3.5寸(注意,這個3.5寸說的是屏幕的對角線寬),這時候device-width就是320px,也是手機的分辨率寬,此時device-width就是設備寬。但第二代的iphone分辨率提高為了480960,屏幕尺寸為依然為3.5寸,如果device-width還是設備寬,那么同樣是320px的頁面放480960的手機屏上,圖文就會變得比較小,又會影響其可讀性。因此iphone的device-width一直維持在320px,ipad一直維持在1024px。這個時候,device-width就不是設備寬了(也就不是分辨率的寬了),是一個中間層。Android采用的也是這一概念,其device-width值以360居多,但也不乏有像540px和600px這樣的奇葩。在設置了<meta />標簽以后,device-width值可以用window.innerWidth*來獲取device-width值。

獲取device-width

經實踐,在手機端(sony z2)以下兩種方法都可以獲取device-width的值:

1. window.innerWidth/innerHeight
2. document.documentElement.clientWidth/clientHeight

//在sony z2下獲取到的值均為(360,513)

引用自Viewport 不權威指南
<blockquote>
其實這個屬性值很有意思,字面意應該是(layout) viewport 寬度等于設備寬度,但在實際中不同的瀏覽器都給出了個定值:320px;這個值還是源于 Apple,因為早期 iPhone 的分辨率為 320px × 480px,大量為 iPhone 量身定制的網站都設置了width=device-width,并且按照寬度 320px 來設計制作,所以其他瀏覽器加入 viewport 支持時為了兼容性也將 device-width 定義為了 320px。
</blockquote>

initial-scale=1

你肯定不知道

<meta name="viewport" content="initial-scale=1">

<meta name="viewport" content="width=device-width">

這兩句代碼能達到一樣的效果,也可以把當前的的viewport變為 ideal viewport。

呵呵,傻眼了吧,因為從理論上來講,這句代碼的作用只是不對當前的頁面進行縮放,也就是頁面本該是多大就是多大。那為什么會有 width=device-width 的效果呢?

要想清楚這件事情,首先你得弄明白這個縮放是相對于什么來縮放的,因為這里的縮放值是1,也就是沒縮放,但卻達到了 ideal viewport 的效果,所以,那答案就只有一個了,縮放是相對于 ideal viewport來進行縮放的,當對ideal viewport進行100%的縮放,也就是縮放值為1的時候,不就得到了 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的寬度)

initial-scale的默認值

好了,現在再來說下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 name="viewport" content="width=device-width">

可以看到通過width=device-width,所有瀏覽器都能把當前的viewport寬度變成ideal viewport的寬度,但要注意的是,在iphone和ipad上,無論是豎屏還是橫屏,寬度都是豎屏時ideal viewport的寬度。

<meta name="viewport" content="initial-scale=1">

測試結果表明 initial-scale=1 也能把當前的viewport寬度變成 ideal viewport 的寬度,但這次輪到了windows phone 上的IE 無論是豎屏還是橫屏都把寬度設為豎屏時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">

viewport的自動調整

其實 viewport 能夠自動調整的。

  • 當 width=320 時,橫屏后的 viewport 寬度仍然是 320px ,但可見區域寬度為 480px ,因此 viewport 要放大為 320 的 1.5 倍,把 320px 的頁面顯示到 480px 的屏幕上,從而頁面顯示的效果就被放大了。
  • 而當 width=device-width 時,橫屏后的 viewport 寬度變為了 480px ,此時的 viewport 沒有縮放, initial-scale 值仍然為 1 ,所以頁面沒有經過縮放。

viewport 自動調整特性:為了讓頁面適應 viewport 的顯示區域進行顯示,瀏覽器會自動根據已設置值的屬性調整其它未設置值的屬性值。

<blockquote>
If you set only some of the properties, then Safari on iOS infers the values of the other properties with the goal of fitting the webpage in the visible area.
</blockquote>

這些可相互影響的屬性值主要包括:width、height、initial-scale。

為什么不制作固定尺寸的頁面,讓瀏覽器自己去縮放

http://segmentfault.com/q/1010000002551392/a-1020000002551691

固定尺寸的頁面:

<meta name="viewport" content="target-densitydpi=device-dpi,width=640,user-scalable=no" />

另外也有這樣的寫法。這是用js的方式。

var phoneScale = parseInt(window.screen.width)/640;
document.write('<meta name="viewport" content="width=640, initial-scale = '+phoneScale+', maximum-scale = '+phoneScale+', maximum-scale = '+phoneScale+', target-densitydpi=device-dpi">');
}

這兩個會讓瀏覽器去縮放。你也知道了,這是縮放。既然是縮放,那么就會失真,這是由于瀏覽器的自身渲染導致的。你發的網頁,我用nexus4測了一下,雖然不是太過明顯,但里面的灰色線條會有粗細不一致的問題,也就是說在某些分辨率也會產生幾條1px的直線看起來不一樣粗的情況。這個問題進一步的引申,就變成了你無法準確的在頁面上畫出規整的直線。

在一些手機上,如果用了一些下載的字體,甚至會發虛(字體的問題你可以找資料仔細看看)。而且一些情況下會有輕微的撕裂和發糊現象,如果你用了部分CSS3的屬性,發糊的現象可能會更嚴重,就是是樓上的說法。還有一個問題就是渲染帶來的卡頓,生產中iPhone表現出了部分頁面滑動不自然。我認為這是各個瀏覽器的實現不一樣帶來的渲染兼容問題,不知事實上如何。

我對這種方案的評價是

夠用,但不完美

參考文獻

Viewport 不權威指南
探索viewport:頁面如何在手機上顯示
移動前端開發之viewport的深入理解
一些關于Viewport與device-width的東西~
Mobile web開發日記
兩個viewport的故事(第一部分)
兩個viewport的故事(第二部分)
自適應網頁設計(Responsive Web Design)
請教viewport的選擇
網頁寬度自動適應手機屏幕寬度的方法

這只是我參考以上文獻后自己的理解,可能會有一些不正確的地方,歡迎指正。

QQ : 459135899

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容