1. 程式人生 > 實用技巧 >移動端適配flexible rem佈局方案

移動端適配flexible rem佈局方案

如果你看了很多篇文章,對 flexible 的解決方案一直還有一種不太瞭解的朦朧感覺,那麼這篇文章會幫你從背景角度一步一步解析為何會有 flexible 這套解決方案,以及這套解決方案是如何作用的。

為什麼不能直接用 px 進行佈局

設計師基於 iphone6(375px 邏輯畫素) 的頁面尺寸給了你一個非常簡單的設計稿

你很快用如下的程式碼實現了,在 chrome 控制檯開啟 iphone6 檢視效果,和設計稿絲毫不差。

<div class="box1"></div>
<div class="box2"></div>
<style>
  .box1 {
    width: 100px;
    height: 100px;
    border: 1px solid red;
    position: fixed;
    left: 10px;
  }

  .box2 {
    width: 100px;
    height: 100px;
    border: 1px solid red;
    position: fixed;
    right: 10px;
  }
</style>

但是選則機型為 iphone5 時頁面變成了這個樣子。

會發現在 iphone5 的頁面下兩個方塊的間距和方塊大小比起來變得非常小,這樣我們認為這個網頁在不同螢幕尺寸的手機上產生了不同的顯示效果。
這個時候肯定有同學說了,那這個設計圖我用 vw 去還原就好了。的確使用 vw 還原設計圖是可以做到的,但 vw 目前的相容性仍然有問題,測試很可能甩過來一個 vivo-x7 告訴你頁面亂掉了。

Flexible 是如何解決以上問題的

設計師在 375px ( iphone6 邏輯畫素寬度 ) 的頁面上畫出了這個設計圖,則該 100px 的方塊在不同尺寸的手機上的大小實際應該為(100px/375px)*{螢幕邏輯畫素寬度},然而我們不可能為每一種尺寸都在css上寫一個 px 寬度。這時可以使用 rem,我們可以通過設定根節點的 font-size 來使不同尺寸的手機的 1rem 對應的 px 值不同。

上面的話讀起來很繞,我寫起來很繞,那麼我將 100px 在 iphone5 和 iphone6 上的不同數值列個表

手機型號邏輯畫素寬度根節點 font-sizerem 數值px 數值
iphone5 320 px 64 px (100/75) rem (100/75)*64 px
iphone6 375 px 75 px (100/75) rem (100/75)*75 px

這樣在 iphone6 的螢幕尺寸上以 100px 渲染出來的盒子大小才能在 iphone5 的螢幕尺寸上以相同的比例渲染出來,在 iphone5 上渲染寬度為(100/75)*64px == 85.33px,所以 flexible 的原始碼中refreshRem函式就是在計算不同手機尺寸應當設定的根節點 font-size,如下所示。

function refreshRem() {
  var width = docEl.getBoundingClientRect().width
  if (width / dpr > 540) {
    // 這裡是為了適配 ipad 和 android 平板橫屏的情況(請暫時無視這個 if 語句)
    width = 540 * dpr
  }
  var rem = width / 10 // 將螢幕寬度分為 10 份,每份為 1rem
  docEl.style.fontSize = rem + 'px'
  flexible.rem = win.rem = rem
}

著名的 1px 畫素問題

如果讀這篇文章同學在之前有讀過其他地方的文章會認為我上面的解釋是錯誤的,因為其他的文章在上面均使用的螢幕物理畫素作為基準,並配合設定<viewport content="width=device-width,user-scalable=no,initial-scale=${scale}">實現,其實,如果沒有 1px 畫素的問題是不需要設定initial-scale的。在文章的前半部分著重講解了如何用 rem 實現類似 vw 的效果,下面是關於1px畫素問題的來龍去脈。

什麼是 1px 畫素問題

首先需要區分 物理畫素 和 邏輯畫素,物理畫素即為顯示器上的最小顯示單元,邏輯畫素也就是css畫素,是在網頁上渲染時的最小單位。而物理畫素與邏輯畫素之比 (物理畫素/邏輯畫素) 稱之為 dpr (device pixel ratio),iphone 5的物理畫素寬度為 640px,邏輯畫素寬度為 320px,則 iphone5 的 dpr 為 2。意味著在 css 上寫 1px 的寬度,在 iphone5 的顯示螢幕上實際渲染了 2px 的物理畫素,使得我們在dpr>=2的手機上設定border-width:1px看起來非常的粗。

如何解決 1px 畫素問題

為了解決這個問題,flexible對上面的方案進行了優化。將initial-scale設定為1/dpr,這樣 dpr 為 2 的手機就會將頁面縮小至 1/2 顯示,這個時候設定border-width:1px就真的是 1px 物理畫素。但是不能因為這個修改影響其他地方的佈局,其實解決方案也很簡單,就是將根節點的 font-size = font-size * dpr,來將佈局大小放大回正常的樣子(但實際不是這樣寫的,是因為當設定了 scale = 1/2 時,documentn.documentElement.getBoundingClientRect().width取得邏輯畫素值會增大至 2 倍,所以不需要額外進行根節點的 font-size = font-size * dpr)。

var width = docEl.getBoundingClientRect().width
var rem = width / 10 // 將螢幕寬度分為 10 份,每份為 1rem
docEl.style.fontSize = rem + 'px'

字型不能使用 rem,可能會出現小數

在 flexible 中有這樣一行程式碼docEl.setAttribute('data-dpr', dpr);這樣我們可以在自己的 css 中根據data-dpr來設定不同 dpr (可以理解為頁面縮放比例) 下的字型大小

[data-dpr='1'] .text{
    font-size: 12px;
}
[data-dpr='2'] .text{
    font-size: 24px;
}
[data-dpr='3'] .text{
    font-size: 36px;
}

廣州設計公司https://www.houdianzi.com 我的007辦公資源網站https://www.wode007.com

上面設計圖的正確還原方式

在你的瀏覽器中開啟這段程式碼並在移動端不同機型上進行切換,會得到正確的設計圖還原效果。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <script type="text/JavaScript" src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.2/??flexible_css.js,flexible.js">
  </script>
  <title>Document</title>
</head>

<body>
  <div class="box1"></div>
  <div class="box2"></div>
  <style>
    .box1 {
      width: 2.666666666666667rem; // 100px ÷ 37.5px(375px設計稿上的根 font-size 大小) = 2.666666666666667rem ,使用 css 前處理器的可以寫一個函式
      height: 2.666666666666667rem;
      border: 1px solid red;
      position: fixed;
      left: 0.2666666666666667rem;
      top: 0.2666666666666667rem;
    }

    .box2 {
      width: 2.666666666666667rem;
      height: 2.666666666666667rem;
      border: 1px solid red;
      position: fixed;
      right: 0.2666666666666667rem;
      top: 0.2666666666666667rem;
    }
  </style>
</body>

</html>