喫透移動端 H5 響應式佈局

前言

=====

最近寫第三個移動端 H5 的項目了,準備記錄下自己在 H5 項目中的一些實踐探索。移動端 H5 與 PC 端開發最大的區別之一,大概就是響應式佈局問題。

那麼下面我們來聊聊移動端響應式佈局,瞭解他的來龍去脈,對現有的最佳解決方案探索。

問題

全文將圍繞下面幾個問題進行論述和展開:

由來

概念

什麼是 H5 技術?

H5 這個命名本身是一個很不討巧的命名,咋一眼看上去認爲 HTML5,或者第 5 級標題的標籤,對一些造成一些不小的誤解。

比如:我的一個某後端同事,談論到 H5 很簡單,HTML 之前我也學過一些,以後要是你請假,我來幫你寫。

我是一臉矇蔽,H5 === HTML?

再看看,搜索引擎中 H5 是什麼?(引用來自谷歌詞條第一頁)

如此看來,將 H5 視作 HTML 的大有人在,而 H5 這個概念只在中國特有,所以對外國人來說他們也認爲是 HTML, 所以,對於外國人和非這個領域的人來說,他們存在一樣的誤解。

目前的 H5 算是一個比較大的概念了,我認爲的 H5 技術是一系列移動端 web 前端技術的集合 大致用一個韋恩圖表示如下

我們這裏只談 web 前端中 H5 技術,H5 技術本身是用於移動端的 web 網頁。由於 App 本身有個 “webview” 的容器,在容器裏可以運行 web 前端相關代碼,由此 H5 和原生 App 結合又衍生出來了 Hybrid App 技術

Hybrid App 技術大致原理

這是我給公司同事普及 H5 知識繪製的圖像。

實踐

解決方案一:rem + pxToRem

概念

css 中用於計量的單位有兩種,一種是絕對單位,另一種是相對單位

絕對單位

對於絕對單位,一般來說常用的也就 px, 其他的可能打印需要用到

相對單位

對於相對單位來說,emrem 屬於一對,vwvh 屬於一對。

前一對相對於字體大小,區別在於 rem 相對於根字體,對於我們控制整體的大小相對容易些,所以我們可以使用它來控制整個頁面的縮放。

後一對,相對於視窗的大小,這個將在下一個節中發揮主要作用。

原理

  1. 監聽屏幕視窗的寬度,通過一定比例換算賦值給htmlfont-size。此時,根字體大小就會隨屏幕寬度而變化。

  2. px 轉換成 rem, 常規方案有兩種,一種是利用sass/less中的自定義函數 pxToRem,寫px時,利用pxToRem函數轉換成 rem。另外一種是直接寫px,編譯過程利用插件全部轉成rem。這樣 dom 中元素的大小,就會隨屏幕寬度變化而變化了。

實現

  1. 動態更新根字體大小
const MAX_FONT_SIZE = 420

// 定義最大的屏幕寬度
document.addEventListener('DOMContentLoaded', () => {
  const html = document.querySelector('html')
  let fontSize = window.innerWidth / 10
  fontSize = fontSize > MAX_FONT_SIZE ? MAX_FONT_SIZE : fontSize
  html.style.fontSize = fontSize + 'px'
})
  1. pxrem

pxToRem 方案一

$rootFontSize: 375 / 10;
// 定義 px 轉化爲 rem 的函數
@function px2rem ($px) {
    @return $px / $rootFontSize + rem;
}

.demo {
    width: px2rem(100);
    height: px2rem(100);
}

pxToRem方案二

vue-cli3 中配置 裝 postcss-pxtorem 插件就可以了,其他平臺大致差不多

const autoprefixer = require('autoprefixer')
const pxtorem = require('postcss-pxtorem')
module.exports = { 
  // ...
  css: {
    sourceMap: true,
    loaderOptions: {
      postcss: {
        plugins: [
          autoprefixer(),
          pxtorem({
            rootValue: 37.5,
            propList: ['*']
          })
        ]
      }
    }
  }
}

點擊快速配置 H5 項目工程

繼續探索 postcss-pxtorem 插件源碼,查看它實現的原理

function createPxReplace (rootValue, unitPrecision, minPixelValue) {
    return function (m, $1) {
        if (!$1) return m;
        var pixels = parseFloat($1);
        if (pixels < minPixelValue) return m;
        var fixedVal = toFixed((pixels / rootValue), unitPrecision);
        return (fixedVal === 0) ? '0' : fixedVal + 'rem';
    };
}

px變換成 rem 主要是這個函數,當然裏面有很多可配置的參數, 核心原理和我們方案一差不多,方便在於,不需要每次寫px都要加上一個函數,代碼也清晰很多

是不是所有元素 px 都要轉換成 rem呢?,那可不一定哦,border 中的 px 不應該轉 rem,涉及到另外一個 1px 的問題,上一篇文章詳細論述過,避免 px 轉 rem,將 border 中的 px 大寫成 PX/Px/pX

1px 適配問題請移至 喫透移動端 1px

解決方案二:vh + vw

原理

vw 相對於視窗寬度的單位,隨寬度變化而變化。由此看來,方案一其實是方案二的一種 Hack, 通過使用監聽實現了方案二的效果

實現

與 rem 類似做法,直接使用 postcss-px-to-viewport 插件進行配置, 配置方式也是和  postcss-pxtorem 大同小異

我們看看插件的原理是不是也是一樣的

function createPxReplace(opts, viewportUnit, viewportSize) {
  return function (m, $1) {
    if (!$1) return m;
    var pixels = parseFloat($1);
    if (pixels <= opts.minPixelValue) return m;
    var parsedVal = toFixed((pixels / viewportSize * 100), opts.unitPrecision);
    return parsedVal === 0 ? '0' : parsedVal + viewportUnit;
  };
}

果然呢,連方法名、變量名、代碼邏輯,都一摸一樣哈哈哈,誰抄誰,我就不指出來啦 - -

其他解決方案

方案 缺陷

上面方案均存在致命缺陷,不推薦使用它完成移動端佈局計算。

flex 與 rem 結合使用更佳

兼容性

上述兩種方案,兼容性主要在於 rem,vh,vw 關鍵詞上

rem在移動端表現高達 100%,令人驚歎!

vh vw 表現慘不忍睹

開源庫解決方案

vant 組件庫

ant-design-mobile 組件庫

ant-design-mobile 組件庫仍然使用 px 單位

@hd: 1px; // 基本單位

// 字體尺寸
// ---
@font-size-icontext: 10 * @hd;
@font-size-caption-sm: 12 * @hd;
@font-size-base: 14 * @hd;
@font-size-subhead: 15 * @hd;
@font-size-caption: 16 * @hd;
@font-size-heading: 17 * @hd;

// 圓角
// ---
@radius-xs: 2 * @hd;
@radius-sm: 3 * @hd;
@radius-md: 5 * @hd;
@radius-lg: 7 * @hd;
@radius-circle: 50%;

// 邊框尺寸
// ---
@border-width-sm: 1PX;
@border-width-md: 1PX;
@border-width-lg: 2 * @hd;

vant 組件一樣,還是由開發者來決定到底用哪一種方案 這種把選擇權交給開發者,算是一種開源庫的最靈活的做法了。

總結

通過該文,你大概瞭解 H5 問題的來龍去脈了吧,也明白瞭如何解決移動端響應式佈局問題,如果這票文章能解決你的疑問或者工作中問題,不妨點個贊收藏下。

由於技術水平有限,文章中如有錯誤地方,請在評論區指出,感謝!

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/-RxtXIUYAIbrRudoW_NfzA