移動端適配總結

阿里巴巴TXD發表於2018-12-10

在 web 的世界裡,無線和 PC 的響應式適配其實是兩個世界……

1. 視口 viewport

1.1 viewport 基礎

viewport 解釋為中文就是‘視口’的意思,也就是瀏覽器中用於顯示網頁的區域。在 PC 端,其大小也就是瀏覽器可視區域的大小,所以我們也不會太關注此概念;而在移動端,絕大多數情況下 viewport 都大於瀏覽器可視區,保證 PC 頁面在移動瀏覽器上面的可視性。為提升可視性體驗,針對移動端有了對 viewport 的深入研究。

1.2 viewport 詳解

在移動端有三種型別的 viewport: layoutviewport、visualviewport、idealviewport。具體解釋如下:

  • layoutviewport: 大於實際螢幕, 元素的寬度繼承於 layoutviewport,用於保證網站的外觀特性與桌面瀏覽器一樣。layoutviewport 到底多寬,每個瀏覽器不同。iPhone 的 safari 為 980px,通過 document.documentElement.clientWidth 獲取。
  • visualviewport: 當前顯示在螢幕上的頁面,即瀏覽器可視區域的寬度。
  • idealviewport: 為瀏覽器定義的可完美適配移動端的理想 viewport,固定不變,可以認為是裝置視口寬度。比如 iphone 7 為 375px, iphone 7p 為 414px。

1.3 viewport 設定

我們通過對幾種 viewport 設定可以對網頁的展示進行有效的控制,在移動端我們經常會在 head 標籤中看到這段程式碼:

<meta name='viewport' content='width=device-width,initial-scale=1,user-scale=no' />
複製程式碼

通過對 meta 標籤三個 viewport 的設定,最終使頁面完美展示。下面詳細的闡釋其具體含義:

  • width 設定的是 layoutviewport 的寬度
  • initial-scale 設定頁面的初始縮放值,並且這個初始縮放值是相對於 idealviewport 縮放的,最終得到的結果不僅會決定 visualviewport,還會影響到 layoutviewport
  • user-scalable 是否允許使用者進行縮放的設定

對上面的說明通過公式推導進行進一步的解釋:

// 設定兩個變數:  
viewport_1 = width;  
viewport_2 = idealviewport / initial-scale;

// 則:  
layoutviewport = max{viewport_1, viewport_2};  
visualviewport = viewport_2;
複製程式碼

只要 layoutviewport === visualviewport,頁面下面不會出現滾動條,預設只是把頁面放大或縮小。

1.4 viewport 舉例

以下是通過改變 meta viewport 的幾個引數的值來算取不同的 viewport:

width initial-scale layoutviewport visualviewport idealviewport 是否滾動
- - 980px 980px 375px
device-width 1 375px 375px 375px
device-width 2 375px 188px 375px
device-width 0.5 750px 750px 375px
480px 1 480px 375px 375px
480px 2 480px 188px 375px
480px 0.5 750px 750px 375px

以上是針對 iphone 6/7/8 的測試資料,且無論怎麼設定 viewport 都具有臨界值,即:75 <= layoutviewport <= 10000,75 <= visualviewport <= 1500。

1.5 為什麼要設定 viewport

viewport 的設定不會對 PC 頁面產生影響,但對於移動頁面卻很重要。下面我們舉例來說明:

  1. 媒體查詢 @media 響應式佈局中,會根據媒體查詢功能來適配多端佈局,必須對 viewport 進行設定,否則根據查詢到的尺寸無法正確匹配視覺寬度而導致佈局混亂。如不設定 viewport 引數,多說移動端媒體查詢的結果將是 980px 這個節點佈局的引數,而非我們通常設定的 768px 範圍內的這個佈局引數
  2. 由於目前多數手機的 dpr 都不再是 1,為了產出高保真頁面,我們一般會給出 750px 的設計稿,那麼就需要通過設定 viewport 的引數來進行整體換算,而不是在每次設定尺寸時進行長度的換算。

2. 裝置畫素比 dpr 與 1px 物理畫素

2.1 物理畫素(physical pixel)

手機螢幕上顯示的最小單元,該最小單元具有顏色及亮度的屬性可供設定,iphone6、7、8 為:750 * 1334,iphone6+、7+、8+ 為 1242 * 2208

2.2 裝置獨立畫素(density-indenpendent pixel)

此為邏輯畫素,計算機裝置中的一個點,css 中設定的畫素指的就是該畫素。老早在沒有 retina 屏之前,裝置獨立畫素與物理畫素是相等的。

2.3 裝置畫素比(device pixel ratio)

裝置畫素比(dpr) = 物理畫素/裝置獨立畫素。如 iphone 6、7、8 的 dpr 為 2,那麼一個裝置獨立畫素便為 4 個物理畫素,因此在 css 上設定的 1px 在其螢幕上佔據的是 2個物理畫素,0.5px 對應的才是其所能展示的最小單位。這就是 1px 在 retina 屏上變粗的原因,目前有很多辦法來解決這一問題。

螢幕快照 2018-10-22 下午4.25.50.png | left | 173x165

2.4 1px的物理畫素的解決方案

從第一部分的討論可知 viewport 的 initial-scale 具有縮放頁面的效果。對於 dpr=2 的螢幕,1px壓縮一半便可與1px的裝置畫素比匹配,這就可以通過將縮放比 initial-scale 設定為 0.5=1/2 而實現。以此類推 dpr=3的螢幕可以將 initial-scale設定為 0.33=1/3 來實現。

3. 裝置畫素比 dpr 與 rem 的適配方案

結合 2、3 部分可以實現 1px 的物理畫素這一最小螢幕單位,那在此基礎上如可讓設計通常提供的 750px 設計稿來完美的適配到多種機型上,使用 rem 是一種解決方式。

3.1 rem 如何設定

rem 是相對於根元素 html 的 font-size 來做計算。通常在頁面初始化時載入時通過對document.documentElement.style.fontSize 設定來實現。

3.2 rem 適配規則

通過對 initial-scale = 1/dpr 的設定,已將對螢幕的描述從物理畫素轉化到了物理畫素上了,這將是後續推導的基礎,且設計稿為 750px。

  1. 物理畫素為 750 = 375 * 2,若螢幕等分為 10 份,那麼 1rem = 75px,10rem = 750px;
  2. 物理畫素為 1125 = 375 * 3,若螢幕等分為 10 份,那麼 1rem = 112.5px, 10rem = 1125px;
  3. 物理畫素為 1242 = 414 * 3, 若螢幕等分為 10 份,那麼 1rem = 124.2px, 10rem = 1242px;

因此可推匯出 rem 的設定方式:

 document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + 'px';
複製程式碼

下面我們將 750px 下,1rem 代表的畫素值用 baseFont 表示,則在 baseFont = 75 的情況下,是分成 10 等份的。因此可以將上面的公式通用話一些:

document.documentElement.style.fontSize = document.documentElement.clientWidth / ( 750 / 75 ) + 'px';
複製程式碼

整體設定可參考如下程式碼:

(function (baseFontSize) {
    const _baseFontSize = baseFontSize || 75;
    const ua = navigator.userAgent;
    const matches = ua.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i);
    const isIos = navigator.appVersion.match(/(iphone|ipad|ipod)/gi);
    const dpr = window.devicePixelRatio || 1;
    if (!isIos && !(matches && matches[1] > 534)) {
        // 如果非iOS, 非Android4.3以上, dpr設為1;
        dpr = 1;
    }
    const scale = 1 / dpr;
    const metaEl = document.querySelector('meta[name="viewport"]');
    if (!metaEl) {
        metaEl = document.createElement('meta');
        metaEl.setAttribute('name', 'viewport');
        window.document.head.appendChild(metaEl);
    }
    metaEl.setAttribute('content', 'width=device-width,user-scalable=no,initial-scale=' + scale + ',maximum-scale=' + scale + ',minimum-scale=' + scale);

    document.documentElement.style.fontSize = document.documentElement.clientWidth / (750 / _baseFontSize) + 'px';
})();
複製程式碼

同時為了書寫方便可以直接通過 px 佈局,然後在打包時利用 pxtorem 庫轉化為基於 rem 的佈局。

4. 視口單位適配方案

將視口寬度 window.innerWidth 和視口高度 window.innerHeight 等分為 100 份,且將這裡的視口理解成 idealviewport 更為貼切,並不會隨著 viewport 的不同設定而改變。

  • vw : 1vw 為視口寬度的 1%
  • vh : 1vh 為視口高度的 1%
  • vmin : vw 和 vh 中的較小值
  • vmax : 選取 vw 和 vh 中的較大值

如果設計稿為 750px,那麼 1vw = 7.5px,100vw = 750px。其實設計稿按照設麼都沒多大關係,最終轉化過來的都是相對單位,上面講的 rem 也是對它的模擬。這裡的比例關係也推薦不要自己換算,使用 pxtoviewport 的庫就可以幫我們轉換。當然每種方案都會有其弊端,這裡就不展開討論。

總結

在移動端開發中,理解視口對適配至關重要。因此本文先從視口展開討論,從而引出 1px、rem 及 vw/vh 這些和適配相關的主要話題。下面提供的參考文章會在某些點上更加細化,以供參考。

參考文章

www.quirksmode.org/mobile/view…

www.w3cplus.com/mobile/lib-…

www.w3cplus.com/css/vw-for-…

image | left

相關文章