輕鬆掌握移動端web開發【尺寸適配】經常使用解決方案

keycode

本文主要針對初學移動端web開發的讀者,筆者也是初學者,文中有衆多用詞不當之處望讀者指正。css

前言

從開始作web app開發到如今,一直對移動端的尺寸適配有一種模糊的概念。能說得上來‘媒體查詢’,‘柵格化佈局’,‘流式佈局’等若干技術名詞和實現方式,可是每次本身作web app開發的時候,作出來的產物老是不盡人意,好比在iPhone5上出現文字溢出,調整好的佈局位置在一些小尺寸手機上發現位置很是不對,或是遮蓋了其餘元素,或是換行了。html

若是是以前,我是這樣的作法:前端

不斷寫媒體查詢作兼容,直到PM或者QA滿意爲止。html5

這樣的方法,存在如下幾個問題:git

  1. 難以適應全部的手機屏幕尺寸,老是會有不兼容的尺寸出現,問題仍然存在,只是還沒有被發現。
  2. 太累了,很是折磨人。特別是這些問題通常會集中涌如今上線前被一併提出來,而那個時候正好是壓力最大的時候。

我想了想,爲何會出現治標不治本的狀況:github

  1. 在項目開始的時候沒和UI協調好規範。
  2. 身邊沒有太多的測試機,無法測試得太全面。
  3. 沒有意識到移動端適配是一個棘手的問題。

那麼,有沒有那樣一種一勞永逸全尺寸支持不用動腦子算的移動端尺寸適配方案呢?

**答案固然是有的。**筆者結合了本身所看的幾篇熱門的博客,總結了其中比較有用的幾個知識點,但願能讓讀者更快的掌握並使用這個'一勞永逸的方法'。能偷懶的事情絕對要偷懶。🐷(熱門博客題目如圖,含flexible的github repo) web

屏幕快照 2018-04-12 下午6.15.30

屏幕快照 2018-04-12 下午6.15.44

1523528305234

咱們要達到的效果

  • 直接根據UI的標註視覺稿上面的尺寸進行開發。如標註的是230px, 經過函數將其轉爲rem而不用人工計算。
  • 在大部分的手機機型上看起來的頁面視覺效果都一致。

什麼是rem

一句話歸納: 假如<html>標籤上設置了樣式font-size:16px,那麼 1rem = 16px。 因此:bash

re

與UI的配合

首先,須要和UI小姐姐說一句話:前端工程師

"標註元素的時候請按照750px * 1334px爲準。"app

那麼,你將會拿到一張以下的標註圖:

【核心】動態計算+rem

到這一步,咱們仍然沒有解決核心問題:

  1. 要本身去將px換算成rem。(可能旁邊會放一個計算器)
  2. 全尺寸適配。

接下來,就是最爲核心的環節了,筆者經過步驟圖向你們還原計算的過程。

第一步:假設有三款不一樣長寬的手機。

第二步:把手機的寬分爲10份,那麼上述三款手機的每份寬度是35px/36px/37px。而且將<html>標籤添加不一樣的font-size設置。

即:一份分別爲35px/36px/37px

第三步:根據UI的px標註圖計算出相應的rem:

第四步:rem將轉化成不一樣的px尺寸在不一樣的手機上呈現:(ps:圖中的除法結果算錯了)

經過這樣的方式,便可以在不一樣尺寸的手機上有相同的展現效果。而最cool的地方,是上述整個過程時自動適配的。開發者只需根據UI標註圖無腦寫就好了,不再用擠眉弄眼地對着Chrome Devtools 瘋狂調試了。

代碼實現

把手機的寬分爲10份,那麼上述三款手機的每份寬度是35px/36px/37px。而且將<html>標籤添加不一樣的font-size設置。

經過JavaScript動態計算出當前的屏幕寬度,切割爲10份並將<html>fontSize設置爲1份單位寬度

key-code

document.addEventListener('DOMContentLoaded', function(e) {
    document.getElementsByTagName('html')[0].style.fontSize = window.innerWidth / 10 + 'px';
}, false);

複製代碼

當初始的 HTML 文檔被徹底加載和解析完成以後,DOMContentLoaded 事件被觸發,而無需等待樣式表、圖像和子框架的完成加載。另外一個不一樣的事件 load 應該僅用於檢測一個徹底加載的頁面。 MDN::DOMContentLoaded

根據UI的px標註圖計算出相應的rem

這一步須要使用Sass來定義一個px2rem的工具函數:

scssFunction

// utils.scss
@function px2rem($px){
    $rem : 75px; // '750/10':分紅10份
    @return ($px/$rem) + rem;
}

// foo.scss

.box1 {
 	width: px2rem(320px); // '(320/750) * 10 = 4.266rem'
 }
複製代碼

這樣,咱們在styleSheet中實際生效的是height: 4.266rem,而1rem對應多少px是上述JavaScript代碼根據不一樣的window.innerWidth提早計算好的。這樣就實現了自動適配。

若是你嫌寫 px2rem()也麻煩,那麼能夠把函數名定義簡單一些。

繞不開的viewport和dpr

在寫這篇博客的開始,我曾試圖繞開闡述viewportdpr這個抽象的概念,由於上述的內容已經能夠從一個維度解決大多數問題了。可是,若是想作得更完美,就必須從另外一個維度出發,而這個維度,就是dpr

首先,要區分兩個概念:

  1. 設備的pixels
  2. css的pixels

有這樣一個場景

一位前端工程師敲出了

.box {
    width: 100px;
    height:100px;
}
複製代碼

那麼此時,他的意思是box在咱們的屏幕中佔的實際長寬是100px,在他腦中是這樣的畫面:

項目上線以後,有一個用戶'不懷好意'地使用了放大鏡功能將長寬放大了兩倍,如今就變成了:

你會發現,設備花了200px的長寬來渲染CSS裏面定義的100px的長寬,而設備pixels和樣式pixels的比值,就是dpr,即Device Pixel Ratio,若是對這個概念仍有問題,請查看viewport剖析

dp

咱們你們都知道Retina屏(視網膜屏),之因此看起來這麼高清,就是由於蘋果設備花兩個像素來渲染一個像素的物體,那麼看起來確定更爲精緻。

因此,若是咱們針對dpr=1的書寫了rem2px(100px),那麼在dpr=2的設備看起來將會是被放大了2倍的元素。

那麼,若是咱們可以查詢出當前設備的dpr,而且作相應的縮放就能夠解決這個問題。

舉個例子:某些安卓機的dpr=1,可是UI作標註圖的時候是根據dpr=2來作的,就像咱們上文的750px * 1334px。直接按照750px * 1334px寫出來的元素將會被放大兩倍,那麼咱們就使頁面縮小兩倍,如何控制呢?

用viewport

簡言之,在這裏咱們使用viewport是爲了控制屏幕的縮放。

viewport

var dpr = window.devicePixelRatio;
meta.setAttribute('content', 'initial-scale=' + 1/dpr + ', maximum-scale=' + 1/dpr + ', minimum-scale=' + 1/dpr + ', user-scalable=no'); 
// 幫助理解 若是dpr=2,說明寫的100px渲染成了200px,因此須要縮小至1/2,即1/dpr
複製代碼

另外值得一提的是,UI通常會以750px * 1334px的標準進行設計,由於這樣使得設計稿更加精細。 好比咱們寫了rem2px(375px),那麼會通過下列的過程換算到設備pixels寬度爲390px且dpr=3的手機。

  1. rem2px(375px) ----> 5rem
  2. 5rem -----> 195px (樣式pixels)
  3. 樣式195px ------> 此時看起來(指的就是設備pixels)有195*3 = 585px的長度
  4. 設置dpr=1/3------->此時看起來只有195px

這樣,咱們完成了從dpr維度的適配。

show me the code:

sourceCode

<script>
  var dpr = window.devicePixelRatio;
  var meta = document.createElement('meta');

  // dpr
  meta.setAttribute('content', 'initial-scale=' + 1/dpr + ', maximum-scale=' + 1/dpr + ', minimum-scale=' + 1/dpr + ', user-scalable=no'); 
  document.getElementsByTagName('head')[0].appendChild(meta);

  // rem
  document.addEventListener('DOMContentLoaded', function (e) {
    document.getElementsByTagName('html')[0].style.fontSize = window.innerWidth / 10 + 'px';
  }, false);
</script>
複製代碼

爲了防止全局變量污染或者覆蓋他人的變量,請封裝成模塊再使用。

One More Thing

在寫這篇博客的過程當中,曾糾結過這樣的問題:rem佈局和百分比佈局感受差距不大啊,由於在寫rem的時候是基於把寬度切爲10份後再寫的,就像是1rem = 10% = 10vw同樣。這讓我一度以爲能夠用百分比佈局。後來發現,若是出現盒子嵌套(這種場景太多了),那麼百分比佈局就出現問題了,由於其百分比的參考系選擇的是父元素,因此咱們若是在子盒子裏面定義10%的寬度,指的是針對父盒子的而不是咱們想要的針對整個window.innerWidth10%。而vw的代碼可維護性不如上述的這套方案,且兼容性也沒有rem好(這一點差距不是太大)。

若是想了解更多關於PC端or移動端佈局,請看參考資源&鳴謝板塊。

參考資源&鳴謝

移動端頁面適配方案

使用Flexible實現手淘H5頁面的終端適配

六種佈局+rem佈局的簡介

DOMContentLoaded與load的區別

rem是如何實現的

AlloyTeam 移動web適配利器

viewport剖析

lib-flexible

相關文章
相關標籤/搜索