天天看點

超詳細講解H5移動端适配

作者:不秃頭程式員
超詳細講解H5移動端适配

前言

移動網際網路發展至今,各種移動裝置應運而生,但它們的實體分辨率可以說是五花八門,一般情況UI會為我們提供375尺寸的設計稿,是以為了讓H5頁面能夠在這些不同的裝置上盡量表現的一緻,前端工程師就不得不對頁面進行移動端适配了。

前置知識

在學習移動端适配前我們需要了解一些相關的前置知識。

超詳細講解H5移動端适配

螢幕尺寸

螢幕尺寸指的是以螢幕對角線的長度來計算的,機關是英寸。1英寸=2.54厘米

電子裝置一般都用英寸來描述螢幕的實體大小,比如我們電腦常見的22、27英寸。英寸(inch,縮寫為in)在荷蘭語中的意思指的是大拇指,一英寸就是指普通人的拇指寬度。

像素pixel

從計算機技術的角度來解釋,像素是硬體和軟體所能控制的最小機關。它指顯示屏的畫面上表示出來的最小機關,不是圖畫上的最小機關。一幅圖像通常包含成千上萬個像素,每個像素都有自己的顔色資訊,它們緊密地組合在一起。「一個像素,就是一個點,或者說是一個很小的正方形」。

螢幕分辨率

螢幕分辨率指一個螢幕具體由多少個像素點組成,機關是px。

我們可以看到上圖中有兩種像素:邏輯像素與實體像素,并且它們數值不一樣,還有就是為什麼一般UI給我們提供的設計稿上的分辨率與真實機型的分辨率不一樣,。

實體像素(裝置像素)

在同一個裝置上,他的實體像素是固定的,也就是廠家在生産顯示裝置時就決定的實際點的個數,對于不同裝置實體像素點的大小是不一樣的。(裝置控制顯示的最小機關,我們常說的1920*1080像素分辨素就是用的實體像素機關)

如果都使用實體像素就會帶來問題:舉個例子,21英寸顯示器的分辨率是1440x1080,5.8英寸的iPhone X的分辨率是2436×1125,我們用CSS畫一條線,其長度是20px,如果都以實體像素作為度量機關,那麼在顯示器上看起來正常,在iPhone X螢幕上就變得非常小。

邏輯像素(裝置獨立像素)

OK,其實喬幫主在之前就想到了會有這個問題,蘋果在iPhone4的釋出會上首次提出了Retina Display(視網膜螢幕)的概念,在iPhone4使用的視網膜螢幕中,把4個像素當1個像素使用,這樣讓螢幕看起來更精緻,并且在不同螢幕中,相同的邏輯像素呈現的尺寸是一緻的。是以高分辨率的裝置,多了一個邏輯像素。我們從第一張圖中可以看到不同裝置的邏輯像素仍然是有差異的,隻不過差異沒有實體像素那麼大,于是便誕生了移動端頁面需要适配這個問題。(與裝置無關的邏輯像素,代表可以通過程式控制使用的虛拟像素)

每英寸像素點ppi

**ppi(pixel per inch)**表示每英寸所包含的像素點數目,數值越高,說明螢幕能以更高密度顯示圖像。

它的計算公式為:PPI=√(X^2+Y^2)/ Z (X:長度像素數;Y:寬度像素數;Z:螢幕大小)

ppi在120-160之間的手機被歸為低密度手機,160-240被歸為中密度,240-320被歸為高密度,320以上被歸為超高密度

超詳細講解H5移動端适配

裝置像素比dpr

「dpr(device pixel ratio)」表示裝置像素比,裝置像素/裝置獨立像素,代表裝置獨立像素到裝置像素的轉換關系,在JS中可以通過 window.devicePixelRatio 擷取

計算公式為:DPR = 實體像素/邏輯像素

當裝置像素比為1:1時,使用1(1×1)個裝置像素顯示1個CSS像素;

當裝置像素比為2:1時,使用4(2×2)個裝置像素顯示1個CSS像素;

當裝置像素比為3:1時,使用9(3×3)個裝置像素顯示1個CSS像素。

超詳細講解H5移動端适配

概念關系圖

螢幕尺寸、螢幕分辨率-->對角線分辨率/螢幕尺寸-->螢幕像素密度PPI|裝置像素比dpr = 實體像素 / 裝置獨立像素dip(dp)|viewport: scale|CSS像素px

視口viewport

viewport指的是視口,他是浏覽器或app中webview顯示頁面的區域。一般來講PC端的視口指的是浏覽器視窗區域,而移動端就有點複雜,它有三個視口:

  • 「layout viewport」:布局視口
  • 「visual viewport」:視覺視口
  • 「ideal viewport」:理想視口

布局視口(layout viewport)

它是由浏覽器提出的一種虛拟的布局視口,用來解決頁面在手機上顯示的問題。這種視口可以通過<meta>标簽設定viewport來改變。移動裝置上的浏覽器都會把自己預設的viewport設為980px或1024px(也可能是其它值,這個是由裝置自己決定的),但帶來的後果就是浏覽器會出現橫向滾動條,因為浏覽器可視區域的寬度是比這個預設的viewport的寬度要小的。

我們可以通過document.documentElement.clientWidth來擷取布局視口大小

視覺視口(visual viewport)

它指的是浏覽器的可視區域,也就是我們在移動端裝置上能夠看到的區域。預設與目前浏覽器視窗大小相等,當使用者對浏覽器進行縮放時,不會改變布局視口的大小,但會改變視覺視窗的大小。

超詳細講解H5移動端适配

我們可以通過window.innerWidth來擷取視覺視口大小。

理想視口(ideal viewport)

理想中的視口。這個概念最早由蘋果提出,其他浏覽器廠商陸續跟進,目的是解決在布局視口下頁面元素過小的問題,顯示在理想視口中的頁面具有最理想的寬度,使用者無需進行縮放。所謂理想視口,即頁面繪制區域可以完美适配裝置寬度的視口大小,不需要出現滾動條即可正常檢視網站的所有内容,且文字圖檔清晰,如所有iphone的理想視口寬度都為320px,安卓裝置的理想視口有320px、360px等等。

當頁面縮放比例為100%時,理想視口 = 視覺視口。

我們可以通過screen.width來擷取理想視口大小。

meta viewport

對于移動端頁面,可以采用<meta>标簽來配置視口大小和縮放等。

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />           
  • 「width」:該屬性被用來控制視窗的寬度,可以将width設定為320這樣确切的像素數,也可以設為device-width這樣的關鍵字,表示裝置的實際寬度,一般為了自适應布局,普遍的做法是将width設定為device-width。
  • 「height」:該屬性被用來控制視窗的高度,可以将height設定為640這樣确切的像素數,也可以設為device-height這樣的關鍵字,表示裝置的實際高度,一般不會設定視窗的高度,這樣内容超出的話采用滾動方式浏覽。
  • 「initial-scale」:該屬性用于指定頁面的初始縮放比例,可以配置0.0~10的數字,「initial-scale=1表示不進行縮放,視窗剛好等于理想視窗」,當大于1時表示将視窗進行放大,小于1時表示縮小。這裡隻表示初始視窗縮放值,使用者也可以自己進行縮放,例如雙指拖動手勢縮放或者輕按兩下手勢放大。「安卓裝置上的initial-scale預設值:」 無預設值,一定要設定,這個屬性才會起作用。在iphone和ipad上,無論你給viewport設的寬的是多少,如果「沒有指定預設的縮放值」,則iphone和ipad會「自動計算這個縮放值」,以達到目前頁面不會出現橫向滾動條(或者說viewport的寬度就是螢幕的寬度)的目的。
  • 「maximum-scale」:該屬性表示使用者能夠手動放大的最大比例,可以配置0.0~10的數字。
  • 「minimum-scale」:該屬性類似maximum-scale,用來指定頁面縮小的最小比例。通常情況下,不會定義該屬性的值,頁面太小将難以浏覽。
  • 「user-scalable」:該屬性表示是否允許使用者手動進行縮放,可配置no或者yes。當配置成no時,使用者将不能通過手勢操作的方式對頁面進行縮放。

這裡需要注意的是viewport隻對移動端浏覽器有效,對PC端浏覽器是無效的。

适配與縮放

為了讓移動端頁面獲得更好的顯示效果,我們必須讓布局視口、視覺視口都盡可能等于理想視口,是以我們一般會設定width=device-width,這就相當于讓布局視口等于理想視口;設定initial-scale=1.0,相當于讓視覺視口等于理想視口;

上面提到width可以決定布局視口的寬度,實際上它并不是布局視口的唯一決定性因素,設定initial-scale也有肯能影響到布局視口,因為布局視口寬度取的是width和視覺視口寬度的最大值。

例如:若手機的理想視口寬度為400px,設定width=device-width,initial-scale=2,此時視覺視口寬度 = 理想視口寬度 / initial-scale即200px,布局視口取兩者最大值即device-width 400px。

若設定width=device-width,initial-scale=0.5,此時視覺視口寬度 = 理想視口寬度 / initial-scale即800px,布局視口取兩者最大值即800px。

移動端适配方案

當我們在做H5移動端開發時,用到的最多的機關是PX,也就是CSS像素,當頁面縮放比為1:1時`,一個CSS像素等于一個裝置獨立像素。但CSS像素是很容易被改變的,比如使用者對頁面進行放大,CSS像素會被放大,此時的CSS像素會跨越更多的裝置像素。

頁面縮放系數 = CSS像素 / 裝置獨立像素

rem适配

rem(font size of the root element)是CSS3新增的一個相對機關,是指相對于根元素的字型大小的機關。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
    <title>Document</title>
    <style>
        *{margin:0;padding:0}
        .box{
            width: 10rem;
            height: 4rem;
            background-color: antiquewhite;
            font-size: 0.53rem; /* 20px*/
        }
    </style>
    <script>
        function setRootRem() {
            const root = document.documentElement;
            /** 以iPhone6為例:布局視口為375px,我們把它分成10份,則1rem = 37.5px,
             * 這時UI給定一個元素的寬為375px(裝置獨立像素),
             * 我們隻需要将它設定為375 / 37.5 = 10rem。
            */
            const scale = root.clientWidth / 10
            root.style.fontSize = scale + 'px'  
        }
        setRootRem()
        window.addEventListener('resize', setRootRem)
    </script>
</head>
<body>
    <div class="box">eee</div>
</body>
</html>           
超詳細講解H5移動端适配

Ok,這裡我們可以看到,我們在選用不同裝置進行測試時,根節點的的font-size會随着裝置的布局視口的寬度變化而變化,是以這裡的元素寬度10rem永遠都是等于目前布局視口的寬度,font-size也會随裝置變化而變化。這就是所謂的移動端适配,其實這種方案最早是由阿裡提出來的一個開源移動端适配解決方案flexible,原理非常簡單。

但這樣我們會發現在寫布局的時候會非常複雜,也就是你需要自己手動去計算一下對應的rem值,比如上面的font-size設計稿上是20px,那我們就要計算一下20px對應的rem是多少,按我們上面的規則,「1px = 1/37.5rem」,是以20px應該對應20/37.5 = 0.53rem。是以這種方案我們通常搭配着CSS預處理器使用

「rem搭配CSS預處理器使用」

這裡我就用vue+less來簡單操作一下,具體可以封裝到底層,這裡暫且示範一下原理。

這裡推薦一下使用自制腳手架songyao-cli來快速生成一個vue項目,安裝完依賴後,開始配置less.

/*rem.less*/
@device-width: 375; /*裝置布局視口*/
@rem: (@device-width/10rem);           

然後将@rem配置成less全局變量

//vue.config.js
module.exports = {
    css: {
        loaderOptions:{
            less: {
                additionalData: ` @import '~@/static/rem.less';`
            }
        }
    }
}           

在vue的入口檔案配置計算rem的方法

// toRem.js
export default function() {
    const root = document.documentElement;
    /** 以iPhone6為例:布局視口為375px,我們把它分成10份,則1rem = 37.5px,
     * 這時UI給定一個元素的寬為375px(裝置獨立像素),
     * 我們隻需要将它設定為375 / 37.5 = 10rem。
    */
    const scale = root.clientWidth / 10
    root.style.fontSize = scale + 'px'  
}


//main.js
import Vue from 'vue'
import App from './App.vue'
import toRem from "./utils/toRem" //
toRem()
window.addEventListener('resize', toRem)
Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')           

然後就可以在vue中使用全局變量@rem進行移動端開發了

<template>
    <div class="songyao">
        <h1>{{ username }}</h1>
    <p>
      了解腳手架及腳手架指令請移步個人部落格<br>
      check out the
      <a href="http://47.100.126.169/zmengBlog" target="_blank" rel="noopener">逐夢部落格</a>.
    </p>
    <p>微信公衆号:<span class="wx_name">前端南玖</span></p>
    </div>
</template>

<script>
export default {
    name: 'songyao',
    data() {
        return {
            username: 'songyao-cli(vue 模闆)'
        }
    },
}
</script>

<style lang="less">
.songyao{
    h1{
        font-size: (24/@rem);
    }
    p{
        font-size: (16/@rem);
    }
   .wx_name{
    color:brown;
    }
}

</style>           
超詳細講解H5移動端适配

不過上面這種方案是一種過渡方案,viewport是由蘋果提出的一種方案,之前各大浏覽器對其相容性并不是很好,這才有了這種rem适配方案。

在阿裡開源庫flexible文檔上有這麼一句話:

由于viewport機關得到衆多浏覽器的相容,lib-flexible這個過渡方案已經可以放棄使用,不管是現在的版本還是以前的版本,都存有一定的問題。建議大家開始使用viewport來替代此方。

對,這種方案已經在慢慢被抛棄了,不過還有不少企業在用。

vw、vh适配

vw(Viewport Width)、vh(Viewport Height)是基于視圖視窗的機關,是css3中提出來的,基于視圖視窗的機關。

vh、vw方案即将視覺視口寬度 window.innerWidth和視覺視口高度 window.innerHeight 等分為 100 份。

上面的flexible方案就是模仿這種方案,因為早些時候vw還沒有得到很好的相容。

  • vw(Viewport's width):1vw等于視覺視口的1%
  • vh(Viewport's height) :1vh 為視覺視口高度的1%
  • vmin : vw 和 vh 中的較小值
  • vmax : 選取 vw 和 vh 中的較大值

如果按視覺視口為375px,那麼1vw = 3.75px,這時UI給定一個元素的寬為75px(裝置獨立像素),我們隻需要将它設定為75 / 3.75 = 20vw。

這裡我們同樣可以借助less來實作,不用自己去手動算,算的過程我們交給less就好了,我們直接按照設計稿上去開發就行

// 還是rem.less 我們加一個@vw變量
@device-width: 375;
@rem: (@device-width/10rem);
@vw: (100vw/@device-width);           
<template>
    <div class="songyao">
        <h1>{{ username }}</h1>
    <p>
      了解腳手架及腳手架指令請移步個人部落格<br>
      check out the
      <a href="http://47.100.126.169/zmengBlog" target="_blank" rel="noopener">逐夢部落格</a>.
    </p>
    <p>微信公衆号:<span class="wx_name">前端南玖</span></p>
    </div>
</template>

<script>
export default {
    name: 'songyao',
    data() {
        return {
            username: 'songyao-cli(vue 模闆)'
        }
    },
}
</script>

<style lang="less">
.songyao{
    h1{
        // font-size: (24/@rem);
        font-size: 24*@vw;
    }
    p{
        // font-size: (16/@rem);
        font-size: 16*@vw;
    }
   .wx_name{
    color:brown;
    }
}

</style>           
超詳細講解H5移動端适配

viewport+PX

OK,我們再來說一種flexible團隊推薦的viewport方案。這種方案可以讓我們在開發時不用關注裝置螢幕尺寸的差異,直接按照設計稿上的标注進行開發,也無需機關的換算,直接用px。

在 HTML 的 head 标簽裡加入 <meta name="viewport" content="width={設計稿寬度}, initial-scale={螢幕邏輯像素寬度/設計稿寬度}" > 。

假如UI給我們提供的設計稿寬度時375px,我們則需要将頁面的viewport的width設為375,然後再根據裝置的邏輯像素将頁面進行整體放縮。

export function initViewport() {
    const width = 375;  // 設計稿寬度
    const scale = window.innerWidth / width
    // console.log('scale', scale)
    let meta = document.querySelector('meta[name=viewport]')
    let content = `width=${width}, init-scale=${scale}, user-scalable=no`
    if(!meta) {
        meta = document.createElement('meta')
        meta.setAttribute('name', 'viewport')
        document.head.appendChild(meta)
    }
    meta.setAttribute('content', content)
}           
<template>
    <div class="songyao">
        <h1 class="name_rem">{{ username }}</h1>
        <h1 class="name_vw">{{ username }}</h1>
        <h1 class="name_px">{{ username }}</h1>
    <p>
      了解腳手架及腳手架指令請移步個人部落格<br>
      check out the
      <a href="http://47.100.126.169/zmengBlog" target="_blank" rel="noopener">逐夢部落格</a>.
    </p>
    <p>微信公衆号:<span class="wx_name">前端南玖</span></p>
    </div>
</template>

<script>
export default {
    name: 'songyao',
    data() {
        return {
            username: 'songyao-cli(vue 模闆)'
        }
    },
}
</script>

<style lang="less">
.songyao{
    p{
        // font-size: (16/@rem);
        font-size: 16*@vw;
    }
    .name_rem{
        font-size: (24/@rem);
    }
    .name_vw{
        font-size: 24*@vw;
    }
    .name_px{
        font-size: 24px;
    }
   .wx_name{
    color:brown;
    }
}

</style>           

這裡我們将三種方案放在一起對比一下,都是對應375設計稿上24px,三種方案表現出來基本一緻。

超詳細講解H5移動端适配

總結

目前來講這三種方案是現在用的最多的方案,它們都有各自的優缺點。

「rem方案」

  • 适配原理稍複雜
  • 需要使用 JS
  • 設計稿标注的 px 換算到 css 的 rem 計算簡單
  • 方案靈活,既能實作整體縮放,又能實作局部不縮放

「vw 方案」

  • 适配原理簡單
  • 不需要 JS 即可适配
  • 設計稿标注的 px 換算到 css 的 vw 計算複雜
  • 方案靈活,既能實作整體縮放,又能實作局部不縮放

「viewport+px方案」

  • 适配原理簡單
  • 需要使用 JS
  • 直接使用設計稿标注無需換算
  • 方案死闆,隻能實作頁面級别肢體縮放

繼續閱讀