移动端 flexible.js 布局详解
原本想直接引入原文鏈接,但是又擔心作者哪天想不開注銷賬號,這么好的一篇文章看不到了,還是轉載一下吧(/ω\)。
 另外推薦一篇好文:移動端rem自適應實操講解
本文講的通過 flexible.js 實現了rem自適應,有了 flexible.js,我們就不必再為移動端各種設備兼容煩惱,flexible.js 是如何通過 rem 實現自適應的呢?一起來看看: 通過 rem 與 px 的換算,你可以把設計稿從 px 轉到 rem。再也不用為各種設備橫行而擔憂。
rem是相對于根元素 <html>,這樣就意味著,我們只需要在根元素確定一個 px 字號,則可以來算出元素的寬高。1rem=16px(瀏覽器html的像素,可以設定這個基準值),假如瀏覽器的 html 設為 64px,則下面的元素則 1rem=64px 來運算。 阿里團隊開源的一個庫。使用 flexible.js 輕松搞定各種不同的移動端設備兼容自適應問題
實現方法
通過js來調整html的字體大小,而在頁面中的制作稿則統一使用rem這個單位來制作。關鍵代碼如下:
;(function(win, lib) {var doc = win.document;var docEl = doc.documentElement;var metaEl = doc.querySelector('meta[name="viewport"]');var flexibleEl = doc.querySelector('meta[name="flexible"]');var dpr = 0;var scale = 0;var tid;var flexible = lib.flexible || (lib.flexible = {});if (metaEl) {console.warn('將根據已有的meta標簽來設置縮放比例');var match = metaEl.getAttribute('content').match(/initial-scale=([d.]+)/);if (match) {scale = parseFloat(match[1]);dpr = parseInt(1 / scale);}} else if (flexibleEl) {var content = flexibleEl.getAttribute('content');if (content) {var initialDpr = content.match(/initial-dpr=([d.]+)/);var maximumDpr = content.match(/maximum-dpr=([d.]+)/);if (initialDpr) {dpr = parseFloat(initialDpr[1]);scale = parseFloat((1 / dpr).toFixed(2)); }if (maximumDpr) {dpr = parseFloat(maximumDpr[1]);scale = parseFloat((1 / dpr).toFixed(2)); }}}if (!dpr && !scale) {var isAndroid = win.navigator.appVersion.match(/android/gi);var isIPhone = win.navigator.appVersion.match(/iphone/gi);var devicePixelRatio = win.devicePixelRatio;if (isIPhone) {// iOS下,對于2和3的屏,用2倍的方案,其余的用1倍方案if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) { dpr = 3;} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){dpr = 2;} else {dpr = 1;}} else {// 其他設備下,仍舊使用1倍的方案dpr = 1;}scale = 1 / dpr;}docEl.setAttribute('data-dpr', dpr);if (!metaEl) {metaEl = doc.createElement('meta');metaEl.setAttribute('name', 'viewport');metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');if (docEl.firstElementChild) {docEl.firstElementChild.appendChild(metaEl);} else {var wrap = doc.createElement('div');wrap.appendChild(metaEl);doc.write(wrap.innerHTML);}}function refreshRem(){var width = docEl.getBoundingClientRect().width;if (width / dpr > 540) {width = 540 * dpr;}var rem = width / 10;docEl.style.fontSize = rem + 'px';flexible.rem = win.rem = rem;}win.addEventListener('resize', function() {clearTimeout(tid);tid = setTimeout(refreshRem, 300);}, false);win.addEventListener('pageshow', function(e) {if (e.persisted) {clearTimeout(tid);tid = setTimeout(refreshRem, 300);}}, false);if (doc.readyState === 'complete') {doc.body.style.fontSize = 12 * dpr + 'px';} else {doc.addEventListener('DOMContentLoaded', function(e) {doc.body.style.fontSize = 12 * dpr + 'px';}, false);}refreshRem();flexible.dpr = win.dpr = dpr;flexible.refreshRem = refreshRem;flexible.rem2px = function(d) {var val = parseFloat(d) * this.rem;if (typeof d === 'string' && d.match(/rem$/)) {val += 'px';}return val;}flexible.px2rem = function(d) {var val = parseFloat(d) / this.rem;if (typeof d === 'string' && d.match(/px$/)) {val += 'rem';}return val;}})(window, window['lib'] || (window['lib'] = {}));從上面的代碼,主要是改變了 dpx 和 document 的 font-size 大小。大小為 docEl.getBoundingClientRect().width / 10 + 'px';, 假設我們的設計稿寬是 640 的,則 html 的字體大小則被設為 64px 。則相當于1rem=64px。 假如一個元素的寬是 160px ,在平時,我們可以采用百分比可以做到自適應,假如使用響應式的話,可能需要設置多個,比如在 320px,輸出 80px,而在 640px 輸出160px 等。 而采用以上 rem 的方法,則只需要輸出 2.5rem 就能實現統一,如下表格:
| Html字體大小 | 32px | 36px | 41.4px | 64px | 
| 實際輸出 | 1rem | 1rem | 1rem | 1rem | 
| 設計稿縮放大小 | 80px | 90px | 103.5px | 160px | 
| 實際輸出 | 2.5rem | 2.5rem | 2.5rem | 2.5rem | 
以上的 2.5rem 是怎么得出的呢? 160/64(1rem的基數為64px)=2.5rem ;按照官方的說法(640px舉例) Flexible 會將視覺稿分成100份 (主要為了以后能更好的兼容 vh 和 vw),而每一份被稱為一個單位 a。同時 1rem 單位被認定為 10a。針對我們這份視覺稿可以計算出:(設計稿為 750px 為例)
1a = 7.5px 1rem = 75px那么我們這個示例的稿子就分成了 10a,也就是整個寬度為 10rem,<html> 對應的 font-size 為 75px: 這樣一來,對于視覺稿上的元素尺寸換算,只需要原始的 px 值除以 rem 基準值即可。例如此例視覺稿中的圖片,其尺寸是 176px * 176px ,轉換成為2.346667rem * 2.346667rem。
本人理解:寫移動端頁面時,讓設計人員將設計稿設計成寬為 640px,方便計算。因為當時的主流是iPhone5及iPhone5s,物理像素寬度為 640,分辨率為320。
 
 如上圖,當我們把分辨率調為 320 時,flexible.js 經過計算后,可以看到 <html> 的 font-size 已經是 32 px了,也就是屏幕寬度 320 除以10的結果。
 而此時設計稿是 640px 寬的,假設某個元素在設計稿上的寬為 128px,那么在實際開發中,我們要把它在 320px 的基礎上計算,即開發時 128px/2/34px=2rem。
 flexible分析,px轉rem的插件工具
另外大漠還寫了一篇詳細的文章:使用 Flexible實現手淘H5頁面的終端適配 里面介紹了一個如何快速轉換rem為px的幾種方法,感興趣的童鞋可以去看看。 也可以看這篇文章:Sass函數功能——rem轉px 另外在使用這個來處理自適應的另一個坑就是css sprite,作者的建議是使用svg,或者icon font.或者base64等其他方案。 另外就是在dpr=2時,小圖片可能會出現模糊,建議以最大的圖片來切圖。
字體建議使用 px
在作者的觀點中,是建議描述性的字體使用px,如果有slogan之類大于48px的,可以使用rem,由于使用rem在iPhone5和iPhone6中字體不同,可能出現13px和15px,點陣字體。 顯然,我們在iPhone3G和iPhone4的Retina屏下面,希望看到的文本字號是相同的。也就是說,我們不希望文本在Retina屏幕下變小,另外,我們希望在大屏手機上看到更多文本,以及,現在絕大多數的字體文件都自帶一些點陣尺寸,通常是16px和24px,所以我們不希望出現13px和15px這樣的奇葩尺寸。 如此一來,就決定了在制作H5的頁面中,rem并不適合用到段落文本上。所以在Flexible整個適配方案中,考慮文本還是使用px作為單位。只不過使用[data-dpr]屬性來區分不同dpr下的文本字號大小。
<style> div { // 默認寫上dpr為1的fontSizewidth: 1rem; height: 0.4rem;font-size: 12px; } [data-dpr="2"] div {font-size: 24px; } [data-dpr="3"] div {font-size: 36px; } </style>為了能更好的利于開發,在實際開發中,我們可以定制一個font-dpr()這樣的Sass混合宏:
<style> @mixin font-dpr($font-size){font-size: $font-size;[data-dpr="2"] & {font-size: $font-size * 2;}[data-dpr="3"] & {font-size: $font-size * 3;} } </style>有了這樣的混合宏之后,在開發中可以直接這樣使用:
@include font-dpr(16px);當然這只是針對于描述性的文本,比如說段落文本。但有的時候文本的字號也需要分場景的,比如在項目中有一個slogan,業務方希望這個slogan能根據不同的終端適配。針對這樣的場景,完全可以使用rem給slogan做計量單位。
由于使用 rem,要根據 rem 的數值來定,就像200*200,在分辨為 320px 下計算成 rem 后會出現小數,而對于字體來說當然不要有小數或者像 13px,15px 這種奇葩的字體,所以這里建議要么把字體 px 定死要么自己手寫媒體查詢吧。
 如果你下載完看過源碼,那你就知道,Flexible對屏幕的寬度做了一個小的限制,在你的屏幕寬度>540px的時候,<html>的 font-size 依然會是 54px,也就是當你把屏幕寬度設置為 640px 的時候,html 的 font-size 是 54px。
 
 所以如果你想兼容更大尺寸的屏幕,那就把這段代碼刪掉好了。
 
總結
以上是生活随笔為你收集整理的移动端 flexible.js 布局详解的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 封装 axios 请求
- 下一篇: iframe 高度根据子页面来确定
