深入淺出 Viewport 設計原理


Viewport 是 HTML5 針對移動端開發新增的一個 meta 屬性, 它的作用是為同一網頁在不同設備的呈現,提供響應式解決方案。這篇文章嘗試通過循序漸進的方式,逐層探索 Viewport 的設計原理,希望能給讀者帶來更加清晰、更加全面的技術認知。

一、引言

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

那么,手機為什么要這么做?解決了什么問題?以及我們開發過程中需要做什么?

下面,我們將帶着這些問題來一步步探索移動端 Viewport 設計原理,以及如何利用 Viewport 進行移動端適配。

 

二、基礎概念

1、屏幕尺寸

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

 

diagonal 就是屏幕對角線的長度,單位是毫米(mm),  然后再把這個長度換算成 “英寸(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 。

現在屏幕上放了一個按鈕,寬度為3px,高度為1px,css 樣式如下:

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

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

所以我們會發現,相同尺寸的屏幕,像素點越多,每個物理像素點看起來就越小,從而導致渲染出來的圖像就會越小。

也就是說,設置相同大小的樣式,屏幕的 PPI 越大,渲染出來的圖像就越小。

 

這其實是一個問題。

在移動應用時代,手機的大小和分辨率參差不齊,從而導致 PPI 也不盡相同。

當我們把一個web頁面放到不同設備上瀏覽時,就會出現“大小各異”的效果,這違背了我們對 css 樣式 “所見即所得” 的認知。

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

5、縮放因子 - DPR

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

 

如下圖所示,同樣大小的矩形元素(灰色條),在第一個屏幕上采用 8×1 個物理像素來渲染,而在第二個屏幕上采用 16×2 個物理像素來渲染,在第三個設備上則采用 24×3 個物理像素來渲染。

這樣做的目的是為了讓這個元素在不同設備上看起來差不多大小。

從圖上可以看出,屏幕的 PPI 越大,需要的物理像素就越多。如果以第一個屏幕為基准,三個屏幕對應的物理像素數,可以用一個倍率來表示,即 1x、2x、3x。

通常,我們把這個倍率叫做 “縮放因子”。縮放因子是移動端響應式的關鍵因素。

而在軟件開發過程中,我們所說的“DPR”其實指的就是縮放因子。 DPR 是 “device pixel ratio” 的縮寫,即設備像素比。

這里需要注意的是:

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

6、DPR 和 PPI 的對應關系

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

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

在 iPhone 中,PPI=163 是 1x 屏;PPI=326 是 2x 屏;PPI=401 是 3x 屏;PPI=458 也是 3x 屏,對應的 DPR 分別為 1、2、3、3。

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

可以看出: 在 Android 上,DPR 和 PPI 基本上呈現為一個固定關系,但未來出現的屏幕未必會遵循這個規律。

所以,有這樣一個重要結論:

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 成正比。

即:邏輯像素 = 物理像素  / DPR

 

有了這個公式,我們就能推導出屏幕的邏輯分辨率,也就是屏幕的邏輯寬度邏輯高度

  • 邏輯寬度 = 水平物理像素 / DPR
  • 邏輯高度 = 垂直物理像素 / DPR

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

// 邏輯寬度
width = 750 / 2 = 375px
// 邏輯高度
height = 1334 / 2 = 667px

因此,iPhone6 的邏輯分辨率為 375 × 667 。在JavaScript中,也可以通過 DOM API 來獲取屏幕的邏輯分辨率:

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

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

8、iPhone 常見的幾種規格

 

三、Viewport

1、Viewport 到底是什么?

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

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

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

 

下面,我們將逐個理解 viewport 中的每個概念。

2、Viewport 畫布

瀏覽器會先把頁面內容繪制到畫布上,然后再通過屏幕窗口呈現出來。

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

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

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

3、device-width 指的是什么?

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

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

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

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

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

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

5、畫布縮放 - scale

scale 是指畫布以 device-width 大小為基准的縮放值。

initial-scale=1.0 也就相當於設置了 width=device-width

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

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

6、動態縮放機制

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

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

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

需要注意的是:

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

7、禁止動態縮放

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

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

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

8、三個 Viewport

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

而把設置 width=device-width 的畫布稱為 ideal viewport,即“理想視口”。

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

 

四、響應式布局方案

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

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

  • 百分比

  • 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,這個問題就不難解決了。

 

原創發布  @一像素   2020.01.06 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM