Viewport 設計原理

Viewport 是 HTML5 針對移動端開發新增的一個 meta 屬性, 它的作用是爲同一網頁在不同設備的呈現,提供響應式解決方案=>移動端適配

一、問題

在PC時代,用 css 設置 1px 邊框,顯示器會用1個物理像素進行渲染。而進入移動應用時代後,我們原來設置1px邊框,在手機上可能需要用 2 個或 3 個物理像素來渲染。

二、基礎概念

1、屏幕尺寸

屏幕尺寸指的是手機屏幕對角線的長度,知道了屏幕的寬度(width)和高度(height),對角線就可以通過勾股定理算出:

 

然後再把這個長度換算成 “英寸(inch)”,就是我們平時所說的手機尺寸。

1 英寸等於 25.4mm,即:

比如 iPhone 常見的尺寸有 3.5寸、4寸、4.7寸、5.5寸 等等。

2、物理像素

我們在手機屏幕上看到的畫面,本質上都是由一個個發光的物理像素組成,物理像素是構成屏幕圖像的最小單元。我們常說的屏幕分辨率,就是指這個屏幕上擁有多少個物理像素。

比如: iPhone4 的分辨率是 640 × 960,即屏幕在水平方向上有 640 個像素,在垂直方向上有 960 個像素。

通常,設計師給的 UI 設計稿上的 PX 指的就是物理像素。

3、像素密度 - PPI

PPI(Pixel Per Inch by diagonal):表示對角線上每英寸所擁有的像素個數。

計算PPI,可以簡單用勾股定理計算出對角線上的像素,再除以對角線長度:

將 iPhone 4 屏幕數據代入公式,即可得出 iPhone4 的 PPI :

PPI 的值越大,每英寸屏幕上的物理像素點就越多越密集,從而渲染出來的畫面也更加細膩、清晰。

 

比如,iphone3GS 和 iphone4 擁有相同大小的屏幕。但前者的分辨率是 320*480,可以算出PPI爲 163,而後者的分辨率是 640*960, 其PPI是326。

這就導致 iphone4 在畫面呈現上比 iphone3GS 更加清晰和細膩。

4、PPI 導致的問題

我們先看下面的兩張圖有什麼區別?

           

很明細,左邊的圖要比右邊的看着舒服。

左邊的字體大小適中,圖片文字都能看的清楚,相比而言,右邊的字體就太小了,讓用戶閱讀變得困難。

那麼,這個問題是怎麼造成的呢?

我們先來做一個對比實驗,如下圖所示:

 左圖和右圖分別代表兩塊尺寸相同的屏幕,長度和寬度均爲 5cm,屏幕上的每個方格代表一個物理像素點。

唯一不同的是,左邊屏幕分辨率爲5 × 5,而右邊屏幕分辨率爲 10 × 10 。

現在屏幕上放了一個按鈕,它的 css 樣式爲:

.button { width: 3px; height: 1px; }

從圖上的效果可以看出,雖然我們爲兩個按鈕設置了相同的大小,但右屏上的按鈕比左屏上的按鈕小了很多。

發現:相同尺寸的屏幕,像素點越多,每個物理像素點“自身”大小就越小,從而導致渲染出來的圖像就會越小。也就是說,設置相同大小的樣式,屏幕的 PPI 越大,渲染出來的圖像就越小。

這其實是一個問題。

在移動時代,手機的大小和分辨率參差不齊,從而導致PPI也不盡相同。當我們把一個web頁面放到PPI不同的設備上瀏覽時,就會出現“大小各異”的效果,違背了我們對 css 樣式 “所見即所得” 的認知。

爲了讓同一個元素在所有設備上看起來都差不多大,設備廠商給顯示屏幕增加了 “縮放因子”。

5、縮放因子 - DPR

這裏的縮放因子並不是對圖像本身進行縮放,而是使用更多的像素來渲染同一個元素。

如下圖所示,同樣大小的矩形,在第一個設備上用過了 8×1 個物理像素來渲染,而在第二個設備上用了 16×2 個物理像素來渲染,在第三個設備上則用了 24×3 個物理像素來渲染。

這樣以來,同一個元素在所有設備上的顯示效果都一樣了!

從圖上可以看出,屏幕的PPI越大,縮放因子就越大。如果以第一個屏幕爲基準,這三個屏幕的縮放因子分別爲: 1、2、3。

通常我們把 “縮放因子” 叫做 DPR。DPR 是 device pixel ratio 的縮寫,即設備像素比。

注意:dpr 的大小並不是通過固定公式計算出來的,而是廠商給屏幕設置的一個固定值,出廠時就確定了,它的大小不會隨着程序的設置而改變。

6、DPR 和 PPI 的對應關係

不同平臺定義 DPR 的基線 PPI 是不同的。

由於第一代 iPhone 的 PPI 是163,所以蘋果把 163 作爲縮放基線。

因此,在 iPhone 中, PPI=163 是1x 屏,PPI=326 是 2x 屏。

PPI

163

326

401

458

DPR

1

2

3

3

代表機型

iPhone3GS

iPhone4

iPhone6P

iPhoneX

而 Android 的縮放基線 PPI 是160,所以 PPI=160 是 1x 屏,PPI=320 是 2x 屏。

從圖中可以看出:DPR 的大小和 PPI 正相關,但不成正比,我們無法通過特定的公式來計算它的大小。

7、邏輯像素和邏輯分辨率

對於同一個元素,DPR 越大,屏幕所需要的物理像素就越多,這是我們上面得出的結論。

那麼,在軟件程序中,元素的大小到底應該寫成多少px?

爲了解決這個問題,我們引入 “邏輯像素” 的概念,即 css 中寫的 px 。

所謂邏輯像素,就是它的大小和物理像素不是一一對應的。

假設,我們現在設置一個元素的css樣式如下:

.el { width: 8px; height: 1px; }

那麼,這個元素在不同屏幕上渲染方式是不同的:

在 dpr=1 的屏幕上,1個邏輯像素對應1個物理像素。

在 dpr=2 的屏幕上,1個邏輯像素需要對應2個物理像素,才能保證元素同等大小。

同理,在 dpr=3 的屏幕上,1個邏輯像素對應3個物理像素,才能保證元素同等大小。

結論:一個邏輯像素在不同屏幕上所表示的物理像素數是不同的,它的大小和 dpr 一一對應。

有了這個理論,我們就能推導出屏幕的邏輯分辨率,也就是屏幕的 “邏輯寬度” 和 “邏輯高度”。

---css中的1px並不等於設備的1px

還有一個因素也會引起css中px的變化,那就是用戶縮放。例如,當用戶把頁面放大一倍,那麼css中1px所代表的物理像素也會增加一倍;反之把頁面縮小一倍,css中1px所代表的物理像素也會減少一倍。

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

devicePixelRatio的測試結果:

比如 iPhone6 的物理分辨率爲 750 × 1334,dpr = 2, 帶入公式就可以得出其邏輯分辨率:

 屏幕的邏輯分辨率也可以通過 DOM API 來獲取:

// iPhone6
window.screen.width;// 375px
window.screen.height;// 667px

通常,我們在 CSS 中設置的元素尺寸,本質上都是基於邏輯分辨率進行佈局的。

8、iPhone 常見的幾種規格

設備

邏輯分辨率(point)

物理分辨率(pixel)

屏幕尺寸

dpr

PPI

iPhone 3GS

320 × 480

320 × 480

3.5寸

1

163

iPhone 4

320 × 480

640 × 960

3.5寸

2

326

iPhone 5

320 × 568

640 × 1136

4.0寸

2

326

iPhone 6

375 × 667

750 × 1334

4.7寸

2

326

iPhone 6 Plus

414 × 736

1080 × 1920

5.5寸

3

401

iPhone X

375 × 812

1125 × 2436

5.8寸

3

458

iPhoneXR

414 × 896

828 × 1792

6.1寸

2

326

iPhoneXS Max

414 × 896

1242 × 2688

6.5寸

3

458

三、Viewport

1、Viewport 到底是什麼?

我們在寫H5頁面的時候,通常會在 html 的 head 中加入下面這句話:

這句話就是在設置頁面的 viewport 。那 viewport 到底是什麼?爲什麼要設置它?

簡單來說:viewport 是屏幕背後的一張畫布。

2、Viewport 畫布

瀏覽器會先把頁面內容繪製到畫布上(頁面交給虛擬窗口),然後再通過屏幕窗口呈現出來。

畫布的寬度可大可小, 當畫布的寬度大於屏幕寬度時,畫布上的內容就無法通過屏幕全部展示出來,用戶可以通過屏幕手勢來拖動畫布查看被遮擋的部分。

如果沒有在 html 中加 viewport 的設置,畫布其實也是存在的,瀏覽器會給畫布設置一個默認寬度 ,不同平臺的默認值如下:

畫布的寬度可以通過 DOM API 來獲取:

viewport可能比瀏覽器的可視區域要大,也可能比瀏覽器的可視區域要小。在默認情況下,一般來講,移動設備上的viewport都是要大於瀏覽器可視區域的,這是因爲考慮到移動設備的分辨率相對於桌面電腦來說都比較小,所以爲了能在移動設備上正常顯示那些傳統的爲桌面瀏覽器設計的網站(防止pc上的頁面在手機上顯示時因屏幕太小而導致排版亂掉),移動設備上的瀏覽器都會把自己默認的viewport設爲980px或1024px(也可能是其它值,這個是由設備自己決定的),但帶來的後果就是瀏覽器會出現橫向滾動條。

3、device-width 指的是什麼?

device-width 指屏幕可視窗口在水平方向上的邏輯像素。

 

device-width 的大小可以通過 window.screen.width 來獲取:

4、width=device-width 在設置誰的寬度?

width 指的是畫布的寬度,device-width 是可視窗口寬度。

width=device-width 就是把畫布的寬度設置爲可視窗口的寬度,讓畫布上的內容完全呈現出來。

設置了 width=device-width 之後,畫布的寬度就和屏幕的寬度一樣大了。

測試iphone XS:viewport meta有和沒有下width和device-width:

   

5、畫布縮放 scale

scale 是指畫布以 device-width 大小爲基準的縮放值。initial-scale=1.0 也就相當於設置了 width=device-width

通常需要同時設置這兩個值,這是因爲兩者在不同平臺有兼容性問題:

 在iPhone 和 iPad 上,只支持 inital-scale=1 的設置,而在 IE 只支持 width=device-width ,所以兩者同時設置,可以兼容所有的平臺。

總結:這兩者各有一個小缺陷,就是iphone、ipad以及IE 會橫豎屏不分,通通以豎屏的ideal viewport寬度爲準。所以,最完美的寫法應該是,兩者都寫上去,這樣就 initial-scale=1 解決了 iphone、ipad的毛病,width=device-width則解決了IE的毛病

問題:width=device-width 和 initial-scale=1都把當前的viewport寬度變成 ideal viewport 的寬度,那麼width和initial-scale=1兩者同時設置,寬度不一致出現衝突怎麼辦?如下

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

說明:當遇到這種情況時,瀏覽器會取它們兩個中較大的那個值。例如,當width=400,ideal viewport的寬度爲320時,取的是400;當width=400, ideal viewport的寬度爲480時,取的是ideal viewport的寬度。(ps:在uc9瀏覽器中,當initial-scale=1時,無論width屬性的值爲多少,此時viewport的寬度永遠都是ideal viewport的寬度)

IE則根本不甩initial-scale這個屬性,無論你給他設置什麼,initial-scale表現出來的效果永遠是1。

6、動態縮放機制

在沒有給頁面設置 viewport 的情況下,當畫布寬度大於可視窗口的時候,瀏覽器會自動對畫布進行縮放,以適配可視窗口大小。這樣頁面在不滾動的情況下也能呈現全部內容。

下面這個頁面是PC端頁面,沒有做移動端適配,可以看出網頁的內容依然可以完全呈現出來,這是因爲沒有設置 viewport 而觸發了 畫布的動態縮放機制。

通過 DOM API 能計算出瀏覽器確實對畫布進行了縮放:

注意:當沒有設置 viewport 或者 設置了viewport 但沒有設置 scale 的時候,纔會觸發瀏覽器動態縮放機制。

7、禁止動態縮放

給頁面添加 viewport 設置,如下所示:

由於手動設置了 scale 的值,沒有觸發自動縮放機制,瀏覽器直接把寬度爲 980px 的畫布原封不動的展示出來了:

這種情況下需要通過滾動才能查看畫布全部內容。

8、三個 Viewport

通常,我們把畫布稱爲 layout viewport, 把屏幕可視窗口稱爲 visual viewport。

而把設置 width=device-width 的畫布稱爲 ideal viewport,即“理想視口”。不需要用戶縮放和橫向滾動條就能正常的查看網站的所有內容,所有的iphone的ideal viewport寬度都是320px,無論它的屏幕寬度是320還是640,也就是說,在iphone中,css中的320px就代表iphone屏幕的寬度。

layout viewpoint:那些爲桌面瀏覽器設計的網站放到移動設備上顯示時,必然會因爲移動設備的viewport太窄,而擠作一團,甚至佈局什麼的都會亂掉=》瀏覽器就決定默認情況下把viewport設爲一個較寬的值,比如980px,這樣的話即使是那些爲桌面設計的網站也能在移動瀏覽器上正常顯示了。

也許有人會問,現在不是有很多手機分辨率都非常大嗎? 比如768x1024,或1080x1920,那這樣的手機用來顯示爲桌面瀏覽器設計的網站是沒問題的吧?前面我們已經說了,css中的1px並不是代表屏幕上的1px。

我們通常在 html 中設置 viewport 就是爲了得到理想視口,方便用戶閱覽。

  • layout viewport:可以通過 document.documentElement.clientWidth來獲取。
  • visual viewport:可以通過 document.documentElement.innerWidth來獲取。
  • ideal viewport:沒有一個固定的尺寸,不同的設備有不同的ideal viewport。

上面innerWidth在Android 2, Oprea mini 和 UC 8中無法正確獲取。

四、響應式佈局方案

響應式佈局的目標是:用同一套代碼適配所有的設備。

常用的佈局方案有以下幾種:

  • 百分比
  • vw
  • Css Media Query
  • rem
  • flex box

下面是手淘團隊移動端適配的協作模式:

設計師一般會把 iPhone6(750px) 作爲設計稿,設計稿中的元素也都是基於750px進行標註的,當然這裏的 px 指的是物理像素。

開發拿到設計稿後,根據iPhone6的 dpr 把標註中的元素大小換算成 css 中的大小,比如設計稿中按鈕的寬度標註爲40px, 則 css 中應該寫成40/2=20px

然後再根據屏幕的邏輯寬度進行同步縮放(如:rem/vw 方案),就可以實現向上或向下適配所有設備。

五、總結

回顧一下開篇提到的問題,其實不難理解,這是由於屏幕的 dpr 不同導致的。

一般情況下,PC 屏幕 dpr 是 1,即 1個邏輯像素 = 1個物理像素,而移動端的 dpr 通常都是 2 或 3,因此也就需要 2個或 3個物理像素來渲染。這也是 “移動端1px邊框” 的經典問題,理解了 viewport,這個問題就不難解決了。

 

meta viewport 標籤首先是由蘋果公司在其safari瀏覽器中引入的,目的就是解決移動設備的viewport問題。後來安卓以及各大瀏覽器廠商也都紛紛效仿,引入對meta viewport的支持,事實也證明這個東西還是非常有用的。

此外,在安卓中還支持  target-densitydpi  這個私有屬性,它表示目標設備的密度等級,作用是決定css中的1px代表多少物理像素

target-densitydpi 

值可以爲一個數值或 high-dpi 、 medium-dpi、 low-dpi、 device-dpi 這幾個字符串中的一個

特別說明的是,當 target-densitydpi=device-dpi 時, css中的1px會等於物理像素中的1px。

因爲這個屬性只有安卓支持,並且安卓已經決定要廢棄target-densitydpi  這個屬性了,所以這個屬性我們要避免進行使用  。

動態改變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>

 

參考:https://www.bbsmax.com/A/D854El7pdE/ 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章