H5移動端適配方案

在討論適配方案之前,先熟悉幾個概念:

設備像素/物理像素:設備實際物理像素點,是顯示設備中一個最微小的物理部件,每個像素可以根據操作系統設置自己的顏色和亮度。任何設備的物理像素的數量都是固定的。例如(iphone6 750 * 1337) (iphone6plus 1960 * 1080)。注意:設備像素不等於CSS像素

像素密度:屏幕上每英寸可以顯示的像素點的數量,單位是PPI

屏幕分辨率:設備屏幕橫縱向上的物理像素點數的乘積。

CSS像素:CSS像素是一個抽象的單位,主要使用在瀏覽器上,用來精確的度量(確定)Web頁面上的內容。CSS像素被稱爲與設備無關的像素(device-independent像素),簡稱爲“DIPs”,單位px

設備獨立像素(PD/PID):密度無關像素,表示用幾個實際物理像素表示的一個虛擬像素,這個虛擬像素就叫設備獨立像素,如(iphone6 375 * 667) (iphone6P 414 * 736) (iphoneX 375 * 812)。實際開發中常可以認爲“ 設備獨立像素=CSS像素 ”

設備像素比(dpr):設備物理像素與設備獨立像素的比例,可以在JS中獲取window.devicePixelRatio。現在的智能手機dpr一般都大於1,例如iphone6/7/8的dpr=2

佈局視口(layout viewport):網頁佈局的基準窗口,PC瀏覽器上,佈局視口就等於當前瀏覽器的窗口大小(不包括 borders 、 margins、滾動條);移動端,佈局視口被賦予一個默認值,大部分爲 980px。document.documentElement.clientWidth/clientHeight獲取佈局視口的寬高。通過在頁面中添加meta的viewport width值設置佈局視口的尺寸,一般爲device-width

視覺視口(visual viewport):用戶通過屏幕真實看到的區域,默認等於當前瀏覽器的窗口大小。通過window.innerWidth/innerHeight來獲取視覺視口大小

理想視口(ideal viewport):網站頁面在移動端展示的理想大小,上面說到的375 * 667指的就是理想視口尺寸。當頁面不進行縮放時,視覺視口=理想視口。

移動端適配解決的就是讓佈局視口和視覺視口無限接近於理想視口

HTML標籤的viewport說明:

  • width決定佈局視口的寬度,device-width是理想視口的寬度,設置 width=device-width就相當於讓佈局視口等於理想視口;
  • initial-scale=理想視口寬度/視覺視口寬度,設置 initial-scale=1 就相當於讓視覺視口等於理想視口。此時,1個 CSS像素=1個設備獨立像素;如果initial-scale=0.5,且屏幕dpr=2,此時1個CSS像素=1個物理像素
  • width取理想視口和視覺視口中較大的值,如果initial-scale<1(即頁面進行縮小),視覺視口大於理想視口,則width的值是取決於視覺視口

方案一:flexible + rem

  • rem:相對html文檔根節點字體大小的相對單位。Chrome瀏覽器默認字體大小爲16px,即1rem=16px。如果設置html字體大小爲20px,則1rem=20px;
  • 實際開發中爲了方便計算,直接設置1rem=10px。對於iphone6/7/8(750 * 1337)而言,物理像素是750,整個屏幕寬度就是750=10rem,html的font-size=75px

適配步驟:

  1. 安裝lib-flexible
$ cnpm i lib-flexible --save-dev

//main.js
import 'lib-flexible/flexible';

或者直接在html文件中通過淘寶cdn引入

<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.4/??flexible_css.js,flexible.js"></script>

flexible會根據當前設備動態計算:

  • 動態改寫標籤,Retina屏(initial-scale=0.5),非Retina屏(initial-scale=1),flexible只對iPhone進行適配
var devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
    // iOS下,對於2和3的屏,用2倍的方案,其餘的用1倍方案
    if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {                
        dpr = 3;
    } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
        dpr = 2;
    } else {
        dpr = 1;
    }
} else {
    // 其他設備下,仍舊使用1倍的方案
    dpr = 1;
}
var scale = 1 / dpr;

---

var scale = isRetina ? 0.5:1;
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
  • 給元素添加data-dpr屬性,並且動態改寫data-dpr的值;
  • 給元素添加font-size屬性,並且動態改寫font-size的值。
  1. 把css中的px全部改寫成rem
  • 利用Sass函數修改px;
  • 利用PostCSS的px2rem動態編譯px到rem
//1,安裝px2rem-loader
$ cnpm install px2rem-loader --save-dev

//2,將px2rem-loader添加到cssLoaders中(build/util.js)
const px2remLoader = {
    loader: 'px2rem-loader',
    options: {
      remUnit: 75
    }
}
//同時在generateLoaders方法中添加px2remLoader
function generateLoaders (loader, loaderOptions) {
    const loaders = [cssLoader, px2remLoader];
    if (loader) {
      loaders.push({
        loader: loader + '-loader',
        options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap
        })
      })
    }
}

原理:

  1. 動態獲取設備屏幕佈局視口的寬度,將html的font-size設置成width/10。例如iphone6的佈局視口寬度是375,則1rem=37.5px
let rem = document.documentElement.getBoundingClientRect().width / 10;
document.documentElement.style.fontSize= rem + 'px';
  1. 動態獲取設備屏幕的dpr(設備像素比),設置標籤中頁面的放縮比(initial-scale、maximum-scale和minimum-scale的值)
window.devicePixelRatio

FAQ

文本在Retina屏幕下變小,出現13px等奇數像素問題

文本不要採用rem作單位,而應該採用px作單位,使用[data-dpr]屬性來區分不同dpr下的文本字號大小

//定義混入
@mixin font-dpr($font-size){
    font-size: $font-size;

    [data-dpr="2"] & {
        font-size: $font-size * 2;
    }

    [data-dpr="3"] & {
        font-size: $font-size * 3;
    }
}

//使用混入
@include font-dpr(16px);
1px(物理像素)邊框特殊處理

設計師要的1px是一個物理像素,也就是所能設計出來的最細的線,而css中的1px是一個點,dpr爲2的話,那麼就是兩個物理像素了,這就不是設計師要的細線了。也就是1px的border不需要自適應,所以不能用rem,只能用px。蘋果支持0.5px,但是Android不支持,0.5px會被直接忽略。

解決方法:

  1. 藉助 PostCSS的 postcss-write-svg 使用 border-image 和 background-image 創建 svg 的 1px邊框
border_1px {
    height: 2px;
    @rect {
        fill: var(--color, black);
        width: 100%;
        height: 50%;
    }
}
.example { border: 1px solid transparent; border-image: svg(border_1px param(--color #00b1ff)) 2 2 stretch; }
  1. 動態修改scale的值,實現頁面放縮(flexible的設計原理)
const scale = 1 / window.devicePixelRatio;
const viewport = document.querySelector('meta[name="viewport"]');
viewport.setAttribute('content', 'width=device-width,user-scalable=no,initial-scale=' + scale +',maximum-scale='+scale + ',minimum-scale=' + scale);

方案二:viewport

vh、vw方案即將視覺視口寬度 window.innerWidth和視覺視口高度 window.innerHeight 等分爲 100 份。如果視覺視口爲 375px,那麼 1vw=3.75px,這時 UI給定一個元素的寬爲 75px(設備獨立像素),我們只需要將它設置爲 75/3.75=20vw。

適配步驟:

  1. 設置標籤
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">

佈局視口=視覺視口=理想視口,此時對於dpr=2的屏幕,1個CSS像素=1個設備獨立像素=2個物理像素。建議UI設計按照設備獨立像素來
2. CSS中的所有px轉換成vw實現自適應

藉助 PostCSS 的 postcss-px-to-viewport 實現px到vw的換算。這樣一來,我們可以在css中依然採用px設置元素尺寸,插件會自動把px轉換成vw。

//1,安裝postcss-px-to-viewport
$ cnpm install postcss-px-to-viewport postcss-write-svg postcss-cssnext postcss-aspect-ratio-mini --save-dev

//2,根目錄下創建.postcssrc.js PostCSS配置文件
module.exports = {
    "plugins": {
        "postcss-px-to-viewport": {
            unitToConvert: "px", // 默認值`px`,需要轉換的單位
            viewportWidth: 375, // 視窗的寬度,對應設計稿寬度(按照設備獨立像素的設計稿,如果設計稿是750,此處也應該是750)
            viewportHeight: 667, // 視窗的高度, 根據375設備的寬度來指定,一般是667,也可不配置
            unitPrecision: 3, // 指定`px`轉換爲視窗單位值的小數位數,默認值是5
            propList: ["*"], // 轉化爲vw的屬性列表
            viewportUnit: "vw", // 指定需要轉換成視窗單位
            fontViewportUnit: "vw", // 字體使用的視窗單位
            selectorBlaskList: [".ignore-"], // 指定不需要轉換爲視窗單位的類
            mediaQuery: false, // 允許在媒體查詢中轉換`px`
            minPixelValue: 1, // 小於或等於`1px`時不轉換爲視窗單位
            replace: true, // 是否直接更換屬性值而不添加備用屬性
            exclude: [], // 忽略某些文件夾下的文件或特定文件,如node_modules
            landscape: false, // 是否添加根據landscapeWidth生成的媒體查詢條件 @media (orientation: landscape)
            landscapeUnit: "vw", // 橫屏時使用的單位
            landscapeWidth: 1134 // 橫屏時使用的視窗寬度
        },
        "postcss-write-svg": {   //用來處理1pxborder的問題
            uft8: false
        },
        "postcss-cssnext": {},   //自動添加樣式前綴
        "postcss-aspect-ratio-mini": {}, //用來處理元素容器寬高比
    }
}

缺陷:

  • px轉換成 vw不一定能完全整除,會存在一定的計算誤差;
  • 容器設置margin時採用px會造成元素溢出,可以用padding代替margin

適配iphoneX

適配iPhoneX底部小黑條:
  1. meta的viewport屬性viewport-fit設置成cover,即網頁內容完全覆蓋可視窗口;
  2. body設置padding-bottom: constant(safe-area-inset-bottom)和padding-bottom: env(safe-area-inset-bottom);
橫屏豎屏適配:
@media screen and (orientation: portrait){
    /*豎屏...*/
}
@media screen and (orientation: landscape){
    /*橫屏...*/
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章