表格 圖片等 寬度自適應 :width:100%; box-sizing: javascript
基於淘寶適配方案flexible + 翻屏h5 適配方案adaptivecss
原文: http://www.w3cplus.com/mobile/lib-flexible-for-html5-layout.htmlhtml
大漠的文章(簡潔):https://github.com/amfe/article/issues/17html5
giuhub:https://github.com/amfe/lib-flexiblejava
本文主要介紹的是如何使用Flexible庫來完成H5頁面的終端適配。爲何推薦使用Flexible庫來作H5頁面的終端設備適配呢?主要由於這個庫在手淘已經使用了近一年,並且已達到了較爲穩定的狀態。 其實H5適配的方案有不少種,網上有關於這方面的教程也很是的多。移動端適配原理大同小異,大部分是經過控制根元素的font-size值實現設備寬度的適配(一般是寬度)。git
方案一:瀏覽器縮放viewportgithub
簡單方便,像素失真web
var phoneScale = parseInt(window.screen.width)/640; document.write('<meta name="viewport" content="width=640, initial-scale = '+phoneScale+', maximum-scale = '+phoneScale+', maximum-scale = '+phoneScale+', target-densitydpi=device-dpi">'); }
方案二:CSS MediaQuery瀏覽器
易理解,斷點區間,安卓機型問題多,需特殊照顧ruby
@media (min-width:340px) and (max-width: 359px) { html,body{font-size: 66%;} } @media (min-width:360px) and (max-width: 374px) { html,body{font-size: 70%;} } ...
方案三:淘寶flexible
市面流行,經受的住數億網民的考驗
實際上:flexible計算dpr,經過width和dpr計算fontSize;對於dpr不等於1的設備,則經過viewport縮小,fontSize放大,達到設配高清屏的目的。 flexible目前最新也是最穩定版本-0.3.2,實現很簡單,主要作如下三件事:
1.計算dpr,rem值 2.動態改寫meta-viewport標籤 3.動態改寫html元素font-size的值
二、3點基於1的計算結果,那麼問題就轉化爲計算dpr、rem,上源碼: 計算dpr核心代碼
var isIPhone = win.navigator.appVersion.match(/iphone/gi); var devicePixelRatio = win.devicePixelRatio; if (isIPhone) { if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) { dpr = 3; } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){ dpr = 2; } else { dpr = 1; } } else { // 其餘設備下,仍舊使用1倍的方案 dpr = 1; }
IOS下:dpr>=3 ==> 3,dpr>=2 ==> 2; Android下:dpr === 1(有點無語)
計算rem核心代碼
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; }
若是已有viewport或flexible標籤則以此計算dpr(優先配置功能)
var metaEl = doc.querySelector('meta[name="viewport"]'); var flexibleEl = doc.querySelector('meta[name="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)); } } }
動態改寫meta-viewport、html的data-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); } }
若是你對視窗viewport不是很瞭解,建議讀讀《消除viewport的疑惑-移動網頁開發》,寫的很是好! 一般的,移動端頁面會寫上這麼一句:
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1, maximum-scale=1, user-scalable=no" />
flexible的viewport是這樣寫的:
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
讀到這裏寂寞的讀者會發現,flexible沒有直接定義device-width?好吧,上文挑重點說:
<meta name="viewport" content="initial-scale=1"> <meta name="viewport" content="width=device-width">
這兩句代碼能達到同樣的效果,也能夠把當前的的viewport變爲 ideal viewport。縮放是相對於什麼來縮放的?由於這裏的縮放值是1,也就是沒縮放,但卻達到了 ideal viewport 的效果,因此,那答案就只有一個了,縮放是相對於 ideal viewport來進行縮放的,當對ideal viewport進行100%的縮放,也就是縮放值爲1的時候,不就獲得了 ideal viewport嗎?事實證實,的確是這樣的。當二者同時存在時取其最大值。
附:
device-width ==> win.screen.width 始終是豎屏結果 ideal-viewport ==> docEle.getBoundingClientRect().width 橫豎屏結果不同 initial-scale = 1 ==> 100% * ideal-viewport
### 使用 在head引入flexible.js >使用flexible就不要設置metaviewport了,由於會阻斷後面的執行,詳情看源碼;除非你想自定義dpr
//直接加載cdn文件(其中css文件非必須)
<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.4/??flexible_css.js,flexible.js"></script> //或下載到項目目錄下 <script src="/local-assets/js/flexible/flexible-0.3.2.js"></script>
其餘的事都是css的事,根據你的PSD尺寸,如640px寬,那麼rem基準就是640/10=64px,有一個64px高的div就是:height:1rem; 推薦 sublimeText3 cssrem插件自動完成計算
adaptive是基於flexible的h5翻屏適配解決方案,是對flexible的dpr和fontSize的計算結果,進一步精準計算並動態改寫。簡言之:flexible適配設備寬度,adaptive適配設備高度。
一個場景:一副滿屏的背景圖片,怎麼樣纔是適配到各個設備?長寬比不一樣的設備,總要有所取捨,你想 background-size: 100% | contain | cover; 100%會拉伸,contain會留白,cover會裁切。這時候傻逼運營來了:這頁面有bug,圖片顯示不全balabala。我就問你what are you fucking doing? 回到正題,估計作翻屏遇到最多的問題就是頁面元素顯示不全了吧,由於每每瀏覽器五花八門的標題欄和功能區會佔據可視區域。 以本工廠單屏設計稿爲例: 640 * 1136(i5,ISUX給的寬高比則比較合理:320 * 504,減去了標題欄高度), 另外一個極端機子MX4 可視區是384 * 518,問你死了沒那麼這種狀況下,這樣顯示比較合理?顯然,信息確定要顯示全,排版不能亂。要知足此需求,估計只有縮小元素了吧,若是有更好的點子還望不吝賜教。
使用rem的好處就是隻需關心根元素fontSize,flexible已經計算好寬度的fontSize了,如下計算校準值:
var autoScale = function(){ var designW = 640, //設計稿寬 designH = 1136, //設計稿高 winW = docEl.clientWidth, //實際可視區寬 winH = docEl.clientHeight, //實際可視區高 idealFs = parseFloat(docEl.style.fontSize), //理想基準字號(既flexible計算字號) idealH = winW * designH / designW, //理想可視區高(根據設計稿等比縮放計算高度) exactFs = winH * idealFs / idealH, //準確基準字號(最終結果) docEl.style.fontSize = exactFs + 'px'; };
上碼定義了設計稿寬高,實際可視區寬高,理想狀況下:這兩組數據的比值相等,那麼也就沒這裏什麼事了。然鵝這是不可能滴,實際上,iphone5 640 * 1136 的寬高比是比較極端的了,相比絕大部分機子是比較「高」的,因此計算出最終fs結果是變小的。 分析一下以上函數:
1.根據設計稿寬高(已知)和實際可視寬度計算出理想可視區高idealH,所謂理想,既是上文提到的理想寬高比值與設計稿的相等 2.根據理想可視區高idealH計算出準確基準字號exactFs 3.把exactFs賦值給根元素就能夠了
如下爲UC的測試數據,可視寬度(window.innerWidth)可視高度(window.innerHeight)
可視寬度 | 可視高度(含導航欄) | 可視高度(不含導航欄) | dpr | |
---|---|---|---|---|
iphone6 | 375 | 559 | 628 | 2 |
Snote4 | 360 | 519 | 567 | 4 |
MI4 | 360 | 514 | 564 | 4 |
P8 | 360 | 470 | 519 | 2 |
Honor3C | 350 | 518 | 567 | 2 |
Mnote2 | 360 | 519 | 567 | 3 |
MX4 | 384 | 518 | 567 | 3 |
坑1:安卓機獲取可視區高先後不一致 測試中發現,「先後」具體體如今滾動過頁面,首次加載的高度比滾動後二次加載的高度小,得益於以前調研上表時作了大量測試,緣由是滾動後瀏覽器會收起標題欄釋放了佔有的高度,這就致使了頁面元素太大而溢出可視區,不信?試試。那麼既然問題出在滾動上,以其人之道還治其人之身:
//以i6 uc爲例 alert(docEl.clientHeight);//559 win.scrollTo(0,0); alert(docEl.clientHeight);//585,不錯,是我要的結果
在autoScale函數前加入 win.scrollTo(0,0); 哼哼,這把該能夠了吧
var autoScale = function(){ win.scrollTo(0,0); ... }
什麼!仍是不行? 。。。 long long after 我又回頭看了flexible的源碼,才發現
win.addEventListener('resize', function() { clearTimeout(tid); tid = setTimeout(refreshRem, 300); }, false);
天了嚕,win.scrollTo(0,0); 解決的問題,resize又讓他一晚上回到解放前。考慮到手機端resize的狀況不多見,不像pc能夠拖拽,因而爲了配合adaptive,我把resize監聽註釋了。問題解決!
坑2:瀏覽器回退 以前沒考慮到瀏覽器回退的狀況,展哥提供的bug,發現會有放大狀況。曾老師提供思路,因而問題很快解決。瀏覽器回退不會觸發onload但會觸發pageshow,以前adaptive沒有監聽pageshow,而flexible有,因而。。。 加了監聽事件,大功告成。
0.1.2版本加入了橫豎屏判斷和提示,橫屏會添加遮罩,提示「請使用豎屏瀏覽」。
example:http://70.vrm.cn/8
在head引入flexible.js,adaptive.js,有先後依賴關係
<script src="/local-assets/js/flexible/flexible-0.3.2.js"></script> <script src="/local-assets/js/flexible/adaptive-0.1.2.js"></script>
在flexible基礎上無痛修改,其餘該怎麼寫怎麼寫,請參照前文。 另外:若有設計稿尺寸不一致,只需修改 designW,designH的值便可。
等等,wap頁面還要兼容pc? 好吧,pc怎麼排版合適?想了想:高度滿屏,寬度自適應能夠不 pc,wap代碼分管的,直接在pc以上js文件後插入;若是用一份代碼,判斷ua去吧~
<script> (function(win,doc){ var wh = win.innerHeight; var dh = 1136; var dw = 640; var h = wh; var w = dw * wh / dh; var docEl = doc.documentElement; setTimeout(function(){ docEl.style.cssText = 'width:'+ w +'px;height:'+ h +'px;margin:0 auto;font-size:'+ w/10 + 'px';},300); })(window,document); </script>
本文到此結束,開發中遇到的問題均已修復,如發現任何bug或可優化的地方歡迎隨時回覆交流