第132天:移動web端-rem佈局(進階)

 

rem佈局(進階版)

      該方案使用至關簡單,把下面這段已壓縮過的 原生JS(僅1kb,源碼已在文章底部更新,2017/5/3) 放到 HTML 的 head 標籤中便可(注:不要手動設置viewport,該方案自動幫你設置),此方案僅適用於移動端webcss

<script>!function(e){function t(a){if(i[a])return i[a].exports;var n=i[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var i={};return t.m=e,t.c=i,t.p="",t(0)}([function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=window;t["default"]=i.flex=function(normal,e,t){var a=e||100,n=t||1,r=i.document,o=navigator.userAgent,d=o.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i),l=o.match(/U3\/((\d+|\.){5,})/i),c=l&&parseInt(l[1].split(".").join(""),10)>=80,p=navigator.appVersion.match(/(iphone|ipad|ipod)/gi),s=i.devicePixelRatio||1;p||d&&d[1]>534||c||(s=1);var u=normal?1:1/s,m=r.querySelector('meta[name="viewport"]');m||(m=r.createElement("meta"),m.setAttribute("name","viewport"),r.head.appendChild(m)),m.setAttribute("content","width=device-width,user-scalable=no,initial-scale="+u+",maximum-scale="+u+",minimum-scale="+u),r.documentElement.style.fontSize=normal?"50px": a/2*s*n+"px"},e.exports=t["default"]}]);  flex(false,100, 1);</script>

代碼原理

這是阿里團隊的高清方案佈局代碼,所謂高清方案就是根據設備屏幕的DPR(設備像素比,又稱DPPX,好比dpr=2時,表示1個CSS像素由4個物理像素點組成)** 動態設置 html 的font-size, 同時根據設備DPR調整頁面的縮放值,進而達到高清效果**。html

有何優點

  • 引用簡單,佈局簡便
  • 根據設備屏幕的DPR,自動設置最合適的高清縮放。
  • 保證了不一樣設備下視覺體驗的一致性。(老方案是,屏幕越大元素越大;此方案是,屏幕越大,看的越多)
  • 有效解決移動端真實1px問題(這裏的1px 是設備屏幕上的物理像素)

如何使用

毫不是每一個地方都要用rem,rem只適用於固定尺寸!
在至關數量的佈局情境中(好比底部導航元素平分屏幕寬,大尺寸元素),你必須使用百分比或者flex才能完美佈局!
此方案也是默認 1rem = 100px,因此你佈局的時候,徹底能夠按照設計師給你的效果圖寫各類尺寸啦。
好比你在效果圖上量取的某個按鈕元素長 55px, 寬37px ,那你直接能夠這樣寫樣式:react

.myBtn {
   width: 0.55rem;
   height: 0.37rem;
}

下面是源碼和Demo

實踐應用1(請在手機端或者手機模式下瀏覽效果更佳!)
實踐應用2(請在手機端或者手機模式下瀏覽效果更佳!)
示例源碼
在線Demo

常見問題說明

1.問:爲啥手機網頁效果圖寬度是要640或者750的,我非得弄個666的不行?

答:老實說固然能夠,不過爲了規範,640或者750是相對合適的。
拿Iphone 5s 舉例,它的css像素寬度是320px,因爲它的dpr=2,因此它的物理像素寬度爲320 × 2 = 640px,這也就是爲何,你在5s上截了一張圖,在電腦上打開,它的原始寬度是640px的緣由。
那 iphone 6 的截圖寬度呢? 375 × 2 = 750
那 iphone 6 sp 的截圖寬度呢? 414 × 3 = 1242
以此類推,你如今能明白效果圖爲何通常是 640 ,750 甚至是 1242 的緣由了麼?git

2.問:寬度用rem寫的狀況下, 在 iphone6 上沒問題, 在 iphone5上會有橫向滾動條,何解?

答:假設你的效果圖寬度是750,在這個效果圖上可能有一個寬度爲7rem(高清方案默認 1rem = 100px)的元素。咱們知道,高清方案的特色就是幾乎完美還原效果圖,也就是說,你寫了一個寬度爲 7rem 的元素,那麼在目前主流移動設備上都是7rem。然而,iphone 5 的寬度爲640,也就是6.4rem。因而橫向滾動條不可避免的出現了。
怎麼辦呢? 這是我目前推薦的比較安全的方式:若是元素的寬度超過效果圖寬度的一半(效果圖寬爲640或750),果斷使用百分比寬度,或者flex佈局。就像把等屏寬的圖片寬度設爲100%同樣。github

3.問:不是 1rem = 100px嗎,爲何個人代碼寫了一個寬度爲3rem的元素,在電腦端的谷歌瀏覽器上寬度只有150px?

答:先說高清方案代碼,再次強調我們的高清方案代碼是根據設備的dpr動態設置html 的 font-size
若是dpr=1(如電腦端),則html的font-size爲50px,此時 1rem = 50px
若是dpr=2(如iphone 5 和 6),則html的font-size爲100px,此時 1rem = 100px
若是dpr=3(如iphone 6 sp),則html的font-size爲150px,此時 1rem = 150px
若是dpr爲其餘值,即使不是整數,如3.4 , 也是同樣直接將dpr 乘以 50 web

再來講說效果圖,通常來說,咱們的效果圖寬度要麼是640,要麼是750,不管哪個,它們對應設備的dpr=2,此時,1 rem = 50 × 2 = 100px。這也就是爲何高清方案默認1rem = 100px。而將1rem默認100px也是好處多多,能夠幫你快速換算單位,好比在750寬度下的效果圖,某元素寬度爲53px,那麼css寬度直接設爲53/100=0.53rem了。瀏覽器

然而極少狀況下,有設計師將效果圖寬定爲1242px,由於他手裏只有一個iphone 6 sp (dpr = 3),設計完效果圖恰好能夠在他的iphone 6 sp裏查看調整。一切完畢以後,他將這個效果圖交給你來切圖。因爲這個效果圖對應設備的dpr=3,也就是1rem = 50 × 3 = 150px。因此若是你量取了一個寬度爲90px的元素,它的css寬度應該爲 90/150=0.6rem。因爲我們的高清方案默認1rem=100px,爲了還原效果圖,你須要這樣換算。固然,一個技巧就是你能夠直接修改我們的高清方案的默認設置。在代碼的最後 你會看到 flex(false, 100, 1) ,將其修改爲flex(false, 66.66667, 1)就不用那麼麻煩的換算了,此時那個90px的直接寫成0.9rem就能夠了。安全

4.問:在此方案下,我若是引用了別的UI庫,那些UI庫的元素會顯得特別小,如何解決?

答:能夠這樣去理解問題的緣由,若是不用高清方案,別的UI庫的元素在移動設備上(假設這個設備是iphone 5好了)顯示是正常的,這沒有問題,而後咱們在這個設備上將該頁面截圖放到電腦上看,發現寬度是640(問答1解釋過了),根據你的像素眼大體測量,你發現這個設備上的某個字體大小應該是12px,而你在電腦上測量應該是24px。app

如今咱們使用高清方案去還原這個頁面,那麼字體大小應該寫爲 0.24rem 纔對!iphone

因此,若是你引用了其餘的UI庫,爲了兼容高清方案,你須要對該UI庫裏凡是應用px的地方作相應處理,即: a px => a*0.02 rem
(具體處理方式因人而異,有模塊化開發經驗的同窗可以使用相似的 px2rem 的插件去轉化,也能夠徹底手動處理)


然而真實狀況每每更爲複雜,好比,你引入了百度地圖(N個樣式須要處理轉換);或者你引入了一個
framework;又或者你使用了 video 標籤,上面默認的尺寸樣式很難處理。等等這些棘手問題

面對這些狀況,此時咱們的高清方案若是再也不壓縮頁面,那麼以上問題將迎刃而解。
基於這樣的思路,筆者對高清方案的源碼作了以下修改,即添加一個叫作 normal 的參數,由它來控制頁面是否壓縮。
在文章頂部代碼的最後,你會看到 flex(false, 100, 1),默認狀況下頁面是開啓壓縮的。

若是你須要禁止壓縮,因爲咱們的源碼執行後,直接將flex函數掛載到全局變量window上了,此時你直接在須要禁止壓縮的頁面執行 window.flex(true) 就能夠了,而rem的用法保持不變。

有一點美中不足的是,若是禁止了頁面壓縮,高清屏的1像素就不能實現了,若是你必需要實現1像素,那麼自行谷歌:css 0.5像素,有N多的解決方案,這裏再也不贅述。

5.問:有時候字體會不受控制的變大,怎麼辦?

答:在X5新內核Blink中,在排版頁面的時候,會主動對字體進行放大,會檢測頁面中的主字體,當某一塊字體在咱們的斷定規則中,認爲字號較小,而且是頁面中的主要字體,就會採起主動放大的操做。然而這不是咱們想要的,能夠採起給最大高度解決

解決方案:

*, *:before, *:after { max-height: 100000px }

補充:有同窗反映,在一些狀況下 textarea 標籤內的字體大小即使加上上面的方案,字體也會變大,沒法控制。此時你須要給 textarea display 設爲 table 或者 inline-table 便可恢復正常。

6.問:我在底部導航用的flex感受更合適一些,請問這樣子混着用能夠嗎?

答:我們的rem適合寫固定尺寸。其他的根據須要換成flex或者百分比。源碼示例中就有這三種的綜合運用。

7.問:在高清方案下,一個標準的,較爲理想的寬度爲640的頁面效果圖應該是怎樣的?

點擊瀏覽:一個標準的640手機頁面設計稿參考(沒錯,在此方案中,你能夠徹底按照這張設計稿的尺寸寫佈局了。就是這麼簡單!)

8.問:用了這個方案如何使用媒體查詢呢?

通常來說,使用了這個方案是不必用媒體查詢了,
點擊瀏覽:一個標準的640手機頁面設計稿參考(沒錯,在此方案中,你能夠徹底按照這張設計稿的尺寸寫佈局了。就是這麼簡單!)

9.問:能夠提供下這個高清方案的源碼嗎?

 1 'use strict';
 2 
 3 /**
 4  * @param {Boolean} [normal = false] - 默認開啓頁面壓縮以使頁面高清;  
 5  * @param {Number} [baseFontSize = 100] - 基礎fontSize, 默認100px;
 6  * @param {Number} [fontscale = 1] - 有的業務但願能放大必定比例的字體;
 7  */
 8 const win = window;
 9 export default win.flex = (normal, baseFontSize, fontscale) => {
10   const _baseFontSize = baseFontSize || 100;
11   const _fontscale = fontscale || 1;
12 
13   const doc = win.document;
14   const ua = navigator.userAgent;
15   const matches = ua.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i);
16   const UCversion = ua.match(/U3\/((\d+|\.){5,})/i);
17   const isUCHd = UCversion && parseInt(UCversion[1].split('.').join(''), 10) >= 80;
18   const isIos = navigator.appVersion.match(/(iphone|ipad|ipod)/gi);
19   let dpr = win.devicePixelRatio || 1;
20   if (!isIos && !(matches && matches[1] > 534) && !isUCHd) {
21     // 若是非iOS, 非Android4.3以上, 非UC內核, 就不執行高清, dpr設爲1;
22     dpr = 1;
23   }
24   const scale = normal ? 1 : 1 / dpr;
25 
26   let metaEl = doc.querySelector('meta[name="viewport"]');
27   if (!metaEl) {
28     metaEl = doc.createElement('meta');
29     metaEl.setAttribute('name', 'viewport');
30     doc.head.appendChild(metaEl);
31   }
32   metaEl.setAttribute('content', `width=device-width,user-scalable=no,initial-scale=${scale},maximum-scale=${scale},minimum-scale=${scale}`);
33   doc.documentElement.style.fontSize = normal ? '50px' : `${_baseFontSize / 2 * dpr * _fontscale}px`;
34 };
10.問:我在使用 rem 佈局進階方案的時候遇到了XXX的問題,如何解決?
  • 此方案久經考驗,具備廣泛適用性,自身出致命問題的狀況不多,至少筆者是沒遇到過。
  • 絕大多數你遇到的問題,都是因爲對rem佈局理解不到位致使的。
相關文章
相關標籤/搜索