博客 / 詳情

返回

談談 H5 移動端適配原理

前言

歡迎關注同名公眾號《熊的貓》,文章會同步更新,也可快速加入前端交流羣!

H5 移動端 開發的必不可少的一個環節就是 移動端網頁的適配,因為 UI 通常只會提供 大小固定的設計稿,而各種不同移動設備具有不同的頁面分辨率和大小,所以適配的目的就是讓一份設計稿在不同移動設備上表現出一致性。

雖然現如今各種插件都可以幫助我們快速配置完成,例如 lib-flexiblepostcss-pxtorempostcss-px-to-viewport 等等,但不少小夥伴在被問及相關原理時卻很難説清楚,那麼本篇文章我們就一起來探究一下其中原理吧!!!

2916D818.gif

文中有不當之處,歡迎在評論區指正!!!

CSS 中的尺寸單位

在瞭解具體的適配方案之前,我們先把 CSS 中適配會涉及到的相關尺寸單位進行一個瞭解吧,總結起來就是一句話:CSS 中的尺寸單位都是 相對長度單位,只是相對的目標不同

px 像素單位

px 全稱為 pixel(像素,它是相對於 屏幕顯示器分辨率(桌面設定的分辨率,不是顯示器的物理分辨率 而言的,在 相同/不同 的設備上 1px 表示多個 設備像素

一個像素點越大 時, 呈現的圖像就會 越模糊;當一個像素點越小時, 像素點就會 越密集, 呈現的圖像就會 越清晰

![image.png]()

em 相對單位

好多人都認為 em 就是相對於 父元素 font-size,實際上在不同的 CSS 屬性當中使用 em 其相對的目標也是不同,如下:

  • 用於 font-size 中則是相對於 父元素 font-size 大小

    image.png

  • 用於 其他屬性(如 width,height) 中使用是相對於 自身 font-size 大小

    image.png

值得注意的是,若 當前元素/父元素font-size 未設置,由於 font-size 屬性值可被繼承的原因,可逐級向上查找,最終找不到則相對於瀏覽器默認字體大小,即 font-size = 16px

rem (root em) 相對單位

remCSS3 新增的一個相對單位,它只相對於 根元素 htmlfont-size 字體大小,remem 的區別在於:

  • rem 相對於 html 根元素的,因此在 body 標籤裏面設置 font-size 是不起作用的
  • 因此 rem 就可做到 目標元素根元素 間保持 成比例 的大小關係,又可以避免字體大小逐層複合的連鎖反應等,例如公共的字體大小可以在 body 中設置即可
![image.png](/img/bVc9qTy)

image.png!

vw 和 vh

vw 全稱是 viewport width,代表的是 視口的寬度,相對於 視口 viewport 的 寬度

vh 全稱是 viewport height,代表的是 視口的高度,相對於 視口 viewport 的 高度

vwvh 是將 視口 寬/高 都分成 100 份,因此 100vw = 視口寬100vh = 視口高

與之相關的還有 vminvmax 兩個單位
  • vminvmax 代表的是 視口寬度視口高度 中的 最小值最大值
  • vmin = 視口高度 vh 和 寬度 vw 間的最小值
  • vmax = 視口高度 vh 和 寬度 vw 間的最大值

image.png

適配方案

rem 適配(等比適配)

核心原理

  • 設備視口 劃分成 n 份n 可以是 任何正確的值(如 flexible.js 中的 n = 10

    • 設置 設備視口 根元素 htmlfont-size = 設備視口寬 ÷ 份數 n,即得到 設備視口 1 rem 到底表示 多少設備視口 px
  • 設計稿 也同樣劃分成 n 份,此時 設計稿中的 a px 對應 設備視口 b rem 的計算方式為

    • 設備視口 b rem = 設計稿 a px ÷ (設計稿 ÷ n 份)

舉個例子

設備視口寬為 375px

image.png

  • 將設備視口分成 10 份,設置 根元素 html 的 font-size = 375 ÷ 10 = 37.5 px,即 1 rem = 37.5 px

    (function (n = 10){
        const dEl = document.documentElement;
        
        function setRem(){
            const rem = dEl.clientWidth / n;
            dEl.style.fontSize = rem  + 'px';
        }
    
        // 初始化執行
        setRem()
    
        // 視口大小變動時執行
        window.onresize = setRem
    })()
設計稿寬為 750px

image.png

  • 將 設計稿 也分成 10 份,每份大小 = 750 ÷ 10 = 75 px
  • 此時 設計稿 上的 "點我拍照"(font-size: 34px) 的文案轉換成符合 設備視口 對應的 rem 就為:

    image.png

    • 設備視口 中 文字 font-size = 34 ÷ 75 = 0.4533333333333333 rem ≈ 0.45 rem

注意】 一般計算結果(人為計算、插件自動化自動化計算)都不會使用這麼長的小數位,比如 0.4533333333333333 rem ≈ 0.45 rem,此時:

  • 原始值 0.4533333333333333 rem = 0.4533333333333333 * 37.5 = 17 px
  • 保留兩位小數後的值 0.45 rem = 0.45 * 37.5 = 16.875 px

而在肉眼觀察下 16.875 px 和 17 px 是沒有差別的,因此可以忽略不計

amfe-flexiblepostcss-pxtorem 驗證

  • 文本和原始樣式

    image.png

  • amfe-flexible postcss-pxtorem 配置

    image.png

    image.png

  • 最終展示效果

    image.png

早期 rem 適配優化(過期方案,瞭解即可

在早期還沒有各種 CSS 預處理器自動化計算的插件 時(即 還需要人為計算時),上述 rem 適配的方案 有一個缺點,那就是針對 設計稿中不同的 px 的轉換為 設備視口 rem 的計算過程還是比較繁瑣,特別是一些比較難算的數值。

例如,上述將 設計稿 34px 轉換為 設備視口 0.9rem 的例子,對大部分人來説是不能 直接/快速 看到設計稿中 px 的數值就能直接算出其對應的 rem 單位的數值的,因此需要進行優化。

優化 的核心就是 複雜計算 變成 簡單計算,例如在 寬 750 px 的設計稿中

  • 34px 轉換成 rem = 34 ÷ 75,這種不直觀的計算叫 複雜計算,若可以實現 34 ÷ 100 即可得到相應的 rem 的方式,就稱為 簡單計算任何數除以 10 或 100 都很容易口算

也就是説,如果我們希望所有設計稿上 px 的計算都是 簡單計算,那麼我們就需要保證其 渲染結果 是正確的即可,換句話説,就是我們需要調整設備視口的 1 rem = n pxn 的值讓其能夠滿足 34 ÷ 100 = 0.34 rem 的情況下還能夠被正常渲染為 17 px 即可,這無非就涉及到一個數學倍數計算而已,就不過多展開了。

0F0DB5DD.jpg

vw/vh 適配

前面説過,vwvh 是將 視口 寬/高 都分成 100 份,相對於 設備視口 375px 來説 1vw = 375 ÷ 100 = 3.75px

經過了 rem 適配方案 的介紹,vw/vh 這種適配方式就是相當於把替換了原本的 rem 單位,因此,這個方式的計算方式和 rem 的方式 如出一轍,區別就在於:

  • rem 的適配方式支持自定義將設備視口劃分為 n 份,n 可以是任何正確值
  • vw/vh 就是將設備視口劃分為 100 份,不支持自定義

因此,假設將 設計稿 750px 中的 34px 轉換為 設備視口 n vw 就等於 n = 34 ÷ 7.5 = 4.533333333333333 vw ≈ 4.53 vw

CSS 預處理器 — 簡化計算

如果使用的是 CSS 預處理器(Less、Sass),那麼就可以通過定義一個全局的 函數 來幫助我們進行運算,例如:

  • Less 中 px2vw() 的定義:

    // plugin.js
    module.exports = {
      install: function (less, pluginManager, functions) {
        functions.add('px2vw', (param, perVW) => {
          if (!param.value) return '0vw'
          if (!perVW.value) return param.value + 'px'
    
          return Number(param.value) / perVW.value + 'vw'
        })
      },
    }
    
    // 具體使用
    <style lang="less">
      @plugin './plugin.js';
    
      @design-width: 750;
      @per-vw: @design-width / 100;
    
      .text {
        font-size: px2vw(34, @per-vw);
        color: #457fff;
      }
    </style>
  • Sass 中 px2vw() 的定義:

    <style lang="scss">
      $design-width: 750;
      $per-vw: $design-width / 100;
    
      @function px2vw($param) {
        @return $param / $per-vw + 'px';
      }
    
      .text {
        font-size: px2vw(34);
        color: #457fff;
      }
    </style>

    使用 postcss-px-to-viewport 進行驗證

    • 文本和原始樣式

      image.png

  • postcss-px-to-viewport 配置

    image.png

  • 最終展示效果

    image.png

等比縮放 — viewport <meta> 標記

所謂 等比縮放 也就是我們不需要關注 設計稿 px 到底對應多少 設備視口 remvw,在開發時,直接使用 設計稿提供的數據 px 即可,然後在將整體頁面按照 設備視口 / 設計稿 的比例進行 整體縮放

而縮放就需要使用到 使用 "viewport" <meta> 標記 來控制視口的大小和形狀了,例如常見的 <meta> 標記如下:

image.png

很明顯,我們只需要控制其中的 widthinitial-scale 的值即可,它們分別是代表當前設備視口寬度和縮放比的值。

還是用 設計稿 750px設備視口 375px 舉例子,因為我們是直接只用設計稿提供的數據來開發,那麼上面的 width = 設備視口寬initial-scale = 設備視口 / 設計稿

(function (designWidth) {
  const dEl = document.documentElement;
  let meta = document.querySelector("meta[name=viewport]");

  // 頁面中不存在 <meta name="viewport" /> 時,手動創建一個   
  if(!meta) {
    meta = document.createElement('meta');
    meta.setAttribute('name', 'viewport');
    document.head.appendChild(meta);
  }

  function setMetaContent(){
    const deviceWidth = dEl.clientWidth;
    const scale = deviceWidth / designWidth;

    const content = `width=${deviceWidth}, initial-scale=${scale}`;

    meta.setAttribute("content", content);
  }

  setMetaContent();

  window.addEventListener("resize", setMetaContent)

})(750);

image.png

第三方組件庫如何做適配?

不知道你是不是會有一些疑問,比如第三方組件庫的適配方式和我們使用的不一致怎麼辦?

不用擔心,第三方組件庫默認都是直接使用 px 的方式來開發的,因此我們在項目中定義的適配方式就是第三方組件庫的適配方式,不會產生衝突的。

例如,vant-ui 文檔中很貼心的為你列出了 瀏覽器適配方案(詳情可見文檔):

  • Viewport 佈局

    • Vant 默認使用 px 作為樣式單位,如果需要使用 viewport 單位 (vw, vh, vmin, vmax),推薦使用 postcss-px-to-viewport 進行轉換
  • Rem 佈局適配

    • postcss-pxtorem 是一款 PostCSS 插件,用於將 px 單位轉化為 rem 單位
    • lib-flexible 用於設置 rem 基準值
  • 桌面端適配

    • 若需要在桌面端使用 Vant,可以引入 @vant/touch-emulator,這個庫會在桌面端自動將 mouse 事件轉換成對應的 touch 事件,使得組件能夠在桌面端使用

最後

歡迎關注同名公眾號《熊的貓》,文章會同步更新,也可快速加入前端交流羣!

以上就是移動端適配的幾種方式的原理了,知道了這些內容之後,其實就不難發現常用的適配插件不過是幫助我們實現了上述的內容和一些細節,例如 自動計算、自動轉換、判斷機型 等等。

18280F14.jpg

希望本文對你有所幫助!!!

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.