移動端適配問題解決方案

dduke發表於2018-04-23

前言

隨著時間的發展,現在基本上人手一部手機的低頭族。做為前端開發的程式猿,在開發移動端web應用的時候,對面一堆各色尺寸不一樣的螢幕,就有點淡淡的憂傷。

移動端適配問題解決方案
移動端適配問題解決方案
已上是2018年二月份最新的友盟資料,可在這裡檢視詳情

很明顯我們所要實現的就是在上述如此之多的螢幕,都能實現UI大大出的視覺圖上的效果。而要實現這樣的效果主要有兩個難點

  1. 各螢幕適配
  2. Retina屏下的細節處理(主要是1px的問題)

各螢幕適配

各螢幕的適配,有兩種方案,flexible + remvw。這三個單詞是什麼意思,看著很眼熟,但是這兩個方案居然是什麼呢,請允許我細細道來。

flexible + rem

顯而易見,該方案是由rem 以及 flexible組成的。rem (font size of the root element)相對於根元素(即html元素)font-size計算值的倍數,flexible 即 flexible.js, 手淘團隊提供的一個為該方案螢幕適配而寫的一個庫,主要實現的功能就是,根據螢幕的寬度給 html 元素設定一個合適的 font-size 值。

怎麼樣看的不是很懂是吧。讓我來用一個頁面場景再複述一遍。

正常情況下,我們的UI大大會以iphone6的尺寸為標準,做一套視覺效果圖,並在上面進行標註,給到我們的標註圖,如下所示

移動端適配問題解決方案

拿到這個圖 我們該如何下手呢

  1. 先設定 html 元素的 font-size, 這個值我們暫時設定為 font-size: 37.5px,即1rem = 37.5px;
  2. 以iphone6為例子,其螢幕寬度為 750px, 整個螢幕的寬度即 20rem = 37.5 * 20px = 750px;
  3. 此時手機號的輸入框為 490px = 13.06777777rem
  4. 依次將頁面上的px轉換為rem,這樣我們就得到了全是rem為尺寸單位的頁面

到這裡為止是不是就結束了呢 ? 很遺憾的告訴你並不是。為什麼 html 元素的 font-size 要設定為 37.5px呢?現在這個介面在iphone6上能完美顯示了,在iphone5s,iphone6p能完美顯示嗎?(iphone6, iphone6s, iphone7. iphone8螢幕沒有發生變化,本文直接用iphone6代替了。) 上面的兩個問題 我們還有沒解決,這個時候就輪到flexible登場了。只需要像如下引入就可實現用js來自動根據螢幕寬度設定 html元素的font-size的值。

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

引申一下

在上述過程中,你會發現,UI給到我們的一般是px標註的圖,我們將其轉化為rem,這個過程其實會花費很大的計算時間。做為一個合格的程式設計師,我們應該把這種機械性無腦的操作交給計算機來實現。我使用的是PostCss的外掛 postcss-px2rem,這個外掛能讓我們在寫程式碼時候直接寫px,在構建的時候自動將我們所寫的px轉換為rem,大大提升了我們的開發效率。

vw

這個vw的方案,相當而言還比較新。vw 即(viewport width)可視視窗的寬度。之所以把這個方案放在後面來說,是因為viewport在去年(2017年)的時候存在不少的相容性問題,各個瀏覽器的支援並不是很好。但是來到了2018年這個時間點,viewport單位意見得到了眾多瀏覽器的支援(80.45%)。

移動端適配問題解決方案
可以在這裡檢視。

接下來就讓我們來正式瞭解下這個方案吧。 vw既然是一個尺寸單位,那它的寬度等於多少呢?等於1%整個螢幕的寬度。舉個例子,再次以iphone6手機為例,100vw = 750px => 1vw = 7.5px

再一次那上次的介面做示範

移動端適配問題解決方案

  1. 根據定義,我們瞭解了在iphone6手機上 1vw = 7.5px
  2. 此時手機號的輸入框為 490px = 65.333333vw
  3. 依次將頁面上的px轉換為vw,這樣我們就得到了全是vw為尺寸單位的頁面

到這裡為止是不是就結束了呢? 是的就是這麼簡單。

引申一下

跟之前一樣的痛點,我們仍然需要花費大量不必要的計算時間去把標註圖中的px轉換為vw,有沒有類似於postcss-px2rem的工具呢?很榮幸能再次站在巨人的肩膀上,已經有大神寫了了類似的PostCss外掛 postcss-px-to-viewport

1px問題

移動端的螢幕不僅僅解析度有差異,其實還有Retina屏的問題。正常情況下,我們程式碼裡的1px在螢幕上就應該顯示一個畫素點,但是在Retina屏下則不僅僅是一個畫素點。再次拿iphone6為例,其dpr(device pixel ratio)裝置畫素比為2,css中一個1x1的點,其實在iphone6上是2x2的點,並且1px的邊框在devicePixelRatio = 2的Retina屏下會顯示成2px,在iPhone6 Plus下甚至會顯示成3px。

這樣的話,我們就會發現在有些手機上1px明顯跟另外的一些手機的1px粗細不一樣。 其實大多數的小公司不會扣這樣的一個細節,比如說我們公司不會。(^__^) 嘻嘻……

但是作為一個有追求的前端工程師,我們應該儘量的把事情做的完美一點,(ps.像大公司看齊,在大公司這個細節問題其實是不容忽視的,為了自己以後的發展前途,我們有必要把這個細節完善掉的。)

這個問題的解決方案有很多,個人覺得最簡單方面的還是大漠大大的一種解決方案。使用postcss-write-svg外掛,

@svg 1px-border {
  height: 2px;
  @rect {
    fill: var(--color, black);
    width: 100%; height: 50%;
    }
  }
.example {
  border: 1px solid transparent;
  border-image: svg(1px-border param(--color #00b1ff)) 2 2 stretch;
}
複製程式碼

編譯出來就是

.example {
  border: 1px solid transparent;
  border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='2px'%3E%3Crect fill='%2300b1ff' width='100%25' height='50%25'/%3E%3C/svg%3E") 2 2 stretch;
}
複製程式碼

其他

小程式中的螢幕適配

最近在寫小程式,在小程式裡,使用的是小程式的那套,跟平時用的vue全家桶以及react全家桶不一樣,並沒有配置webpack,在這種情況下我們使用postcss其實很困難(反正我是搞不出來/(ㄒoㄒ)/~~)

那該怎麼辦呢,小程式提供了一個自己的單位, rpx(responsive pixel): 可以根據螢幕寬度進行自適應。規定螢幕寬為750rpx。如在 iPhone6 上,螢幕寬度為375px,共有750個物理畫素,則750rpx = 375px = 750物理畫素,1rpx = 0.5px = 1物理畫素。

裝置 rpx換算px (螢幕寬度/750) px換算rpx (750/螢幕寬度)
iPhone5 1rpx = 0.42px 1px = 2.34rpx
iPhone6 1rpx = 0.5px 1px = 2rpx
iPhone6p 1rpx = 0.552px 1px = 1.81rpx

我們直接用拿到iphone6的標註圖,直接寫rpx就好。棒?

總結

如果仔細看一下的話,會發現之前的rem的方案其實就是模擬了vw的實現方案,兩者有非常多的相似之處。在lib-flexible的github上有著這樣的一句話。

由於viewport單位得到眾多瀏覽器的相容,lib-flexible這個過渡方案已經可以放棄使用,不管是現在的版本還是以前的版本,都存有一定的問題。建議大家開始使用viewport來替代此方案。vw的相容方案可以參閱《如何在Vue專案中使用vw實現移動端適配》一文。

我們可以得到一個很明確的資訊,lib-flexible這個方案已經被放棄使用了,我們可以去擁抱vw的那套實現方案。

本文參考了大漠大大在 www.w3cplus.com 裡的多篇文章

  1. 使用Flexible實現手淘H5頁面的終端適配
  2. 再聊移動端頁面的適配
  3. 走向視網膜(Retina)的Web時代
  4. 再談Retina下1px的解決方案
  5. 小程式-wxss

相關文章