本文做者: 文藺
本文地址: http://www.wemlion.com/2015/a...
本文由 @文藺 創做,轉載請保留此聲明。 全部權保留,請勿用於商業目的。javascript
基礎知識參考如下兩篇博客:css
http://isux.tencent.com/web-a...html
http://www.w3cplus.com/css3/d...前端
學習http://m.taobao.com 首頁的實現。html5
最近讀到@大漠的新文章《使用Flexible實現手淘H5頁面的終端適配》,和本部分有點關係。暫且加上來以供參考。(updated 2015-11-24)java
源碼進行美化、解讀以後,基本佈局部分的代碼已經被我還原出來了:(2016-01-13補充:後來才發現,早就開源在github上了)node
!function(win, lib) { var timer, doc = win.document, docElem = doc.documentElement, vpMeta = doc.querySelector('meta[name="viewport"]'), flexMeta = doc.querySelector('meta[name="flexible"]'), dpr = 0, scale = 0, flexible = lib.flexible || (lib.flexible = {}); // 設置了 viewport meta if (vpMeta) { console.warn("將根據已有的meta標籤來設置縮放比例"); var initial = vpMeta.getAttribute("content").match(/initial\-scale=([\d\.]+)/); if (initial) { scale = parseFloat(initial[1]); // 已設置的 initialScale dpr = parseInt(1 / scale); // 設備像素比 devicePixelRatio } } // 設置了 flexible Meta else if (flexMeta) { var flexMetaContent = flexMeta.getAttribute("content"); if (flexMetaContent) { var initial = flexMetaContent.match(/initial\-dpr=([\d\.]+)/), maximum = flexMetaContent.match(/maximum\-dpr=([\d\.]+)/); if (initial) { dpr = parseFloat(initial[1]); scale = parseFloat((1 / dpr).toFixed(2)); } if (maximum) { dpr = parseFloat(maximum[1]); scale = parseFloat((1 / dpr).toFixed(2)); } } } // viewport 或 flexible // meta 均未設置 if (!dpr && !scale) { // QST // 這裏的 第一句有什麼用 ? // 和 Android 有毛關係 ? var u = (win.navigator.appVersion.match(/android/gi), win.navigator.appVersion.match(/iphone/gi)), _dpr = win.devicePixelRatio; // 因此這裏彷佛是將全部 Android 設備都設置爲 1 了 dpr = u ? ( (_dpr >= 3 && (!dpr || dpr >= 3)) ? 3 : (_dpr >= 2 && (!dpr || dpr >= 2)) ? 2 : 1 ) : 1; scale = 1 / dpr; } docElem.setAttribute("data-dpr", dpr); // 插入 viewport meta if (!vpMeta) { vpMeta = doc.createElement("meta"); vpMeta.setAttribute("name", "viewport"); vpMeta.setAttribute("content", "initial-scale=" + scale + ", maximum-scale=" + scale + ", minimum-scale=" + scale + ", user-scalable=no"); if (docElem.firstElementChild) { docElem.firstElementChild.appendChild(vpMeta) } else { var div = doc.createElement("div"); div.appendChild(vpMeta); doc.write(div.innerHTML); } } function setFontSize() { var winWidth = docElem.getBoundingClientRect().width; if (winWidth / dpr > 540) { (winWidth = 540 * dpr); } // 根節點 fontSize 根據寬度決定 var baseSize = winWidth / 10; docElem.style.fontSize = baseSize + "px"; flexible.rem = win.rem = baseSize; } // 調整窗口時重置 win.addEventListener("resize", function() { clearTimeout(timer); timer = setTimeout(setFontSize, 300); }, false); // 這一段是我本身加的 // orientationchange 時也須要重算下吧 win.addEventListener("orientationchange", function() { clearTimeout(timer); timer = setTimeout(setFontSize, 300); }, false); // pageshow // keyword: 倒退 緩存相關 win.addEventListener("pageshow", function(e) { if (e.persisted) { clearTimeout(timer); timer = setTimeout(setFontSize, 300); } }, false); // 設置基準字體 if ("complete" === doc.readyState) { doc.body.style.fontSize = 12 * dpr + "px"; } else { doc.addEventListener("DOMContentLoaded", function() { doc.body.style.fontSize = 12 * dpr + "px"; }, false); } setFontSize(); flexible.dpr = win.dpr = dpr; flexible.refreshRem = setFontSize; flexible.rem2px = function(d) { var c = parseFloat(d) * this.rem; if ("string" == typeof d && d.match(/rem$/)) { c += "px"; } return c; }; flexible.px2rem = function(d) { var c = parseFloat(d) / this.rem; if ("string" == typeof d && d.match(/px$/)) { c += "rem"; } return c; } }(window, window.lib || (window.lib = {}));
注意:
淘寶首頁在iPhone4上設置的initial-scale是0.5(其餘尺寸相似)。android
所以,這句在iPhone4上得出的結果是640:ios
var winWidth = docElem.getBoundingClientRect().width;
正是由於淘寶這種獨特的設置,使得 ios 上 1px邊框的問題完美解決(1px變2px, 又被 initial-scale=0.5
縮小了一半)。css3
使用js動態計算:
!(function(doc, win) { var docEle = doc.documentElement, evt = "onorientationchange" in window ? "orientationchange" : "resize", fn = function() { var width = docEle.clientWidth; width && (docEle.style.fontSize = 20 * (width / 320) + "px"); }; win.addEventListener(evt, fn, false); doc.addEventListener("DOMContentLoaded", fn, false); }(document, window));
使用css3 media query 實現
@media screen and (min-width: 320px) { html {font-size: 14px;} } @media screen and (min-width: 360px) { html {font-size: 16px;} } @media screen and (min-width: 400px) { html {font-size: 18px;} } @media screen and (min-width: 440px) { html {font-size: 20px;} } @media screen and (min-width: 480px) { html {font-size: 22px;} } @media screen and (min-width: 640px) { html {font-size: 28px;} }
使用單位 vw 實現動態計算。
html { font-size: 31.25vw; /* 表達式:100*100vw/320 */ }
不過考慮到國內兼容性的問題,仍是結合媒體查詢來使用比較好。(媒體查詢的斷點暫時是借用上面的例子)
@media screen and (min-width: 320px) { html { font-size: 100px; } } @media screen and (min-width: 360px) { html { font-size: 112.5px; } } @media screen and (min-width: 400px) { html { font-size: 125px; } } @media screen and (min-width: 440px) { html { font-size: 137.5px; } } @media screen and (min-width: 480px) { html { font-size: 150px; } } @media screen and (min-width: 640px) { html { font-size: 200px; } } html { font-size: 31.25vw; }
對以上種種方法的綜合:
一、meta:viewport, 仍是initial-scale爲 1;
二、320px屏幕下,把頁面根元素html的字體大小設置爲50px;
三、鑑於咱們拿到的設計圖目前是640px寬的基準,這樣咱們就不用每次本身除以2了,直接在PS中量就好;
四、寬度什麼的最好仍是用百分比處理;涉及到高度、字體大小之類的則用rem。
eg:
設計稿上,div高度爲40px;那麼css就是 div {height: 0.4rem;}
結果就只剩下一步轉換:設計稿上量的長度轉化爲小數。 50% => 0.5
這種計算,不要太簡單。。。
【方法一】純粹css,支持calc函數的動態計算;不支持的用css媒體查詢斷點,優雅降級。
@media screen and (min-width: 320px) { html { font-size: 50px; } } @media screen and (min-width: 360px) { html { font-size: 56px; } } @media screen and (min-width: 400px) { html { font-size: 63px; } } @media screen and (min-width: 440px) { html { font-size: 69px; } } @media screen and (min-width: 480px) { html { font-size: 75px; } } /** * 2016-01-13 訂正 * 作適當限制 * 大於640的屏幕 固定爲100px * 同時須要對body或者最外層wrapper作max-width: 640px的限制 */ /* @media screen and (min-width: 640px) { html { font-size: 100px; } } html { font-size: 15.625vw; } */ html { font-size: 15.625vw; } @media screen and (min-width: 640px) { html { font-size: 100px; } }
【方法二】腳本動態計算
大前提:
一、initial-scale 爲 1;
二、在項目css中(注意不要被公共的base、common之類的影響了,資源加載順序也是蠻重要的),先把html的fontSize設置爲 50px(或者加上媒體查詢代碼), 避免加載未完成時候樣式錯亂;
/* css */ html {font-size: 50px;}
/* javascript */ !(function(win, doc){ function setFontSize() { // 獲取window 寬度 // zepto實現 $(window).width()就是這麼幹的 var winWidth = window.innerWidth; // doc.documentElement.style.fontSize = (winWidth / 640) * 100 + 'px' ; // 2016-01-13 訂正 // 640寬度以上進行限制 須要css進行配合 var size = (winWidth / 640) * 100; doc.documentElement.style.fontSize = (size < 100 ? size : 100) + 'px' ; } var evt = 'onorientationchange' in win ? 'orientationchange' : 'resize'; var timer = null; win.addEventListener(evt, function () { clearTimeout(timer); timer = setTimeout(setFontSize, 300); }, false); win.addEventListener("pageshow", function(e) { if (e.persisted) { clearTimeout(timer); timer = setTimeout(setFontSize, 300); } }, false); // 初始化 setFontSize(); }(window, document));
嗯。。。
就這麼愉快地結束了。。。
不知道解讀了某寶首頁的一點點代碼,而後發在這裏,會不會有什麼後果。。。
==================================================
寫過這篇博客以後,又陸續讀過幾篇關於佈局的文章。
具體已經忘了,大約是大漠的文章,還有一篇應該是搜車前端的博文,另外應該還有關於手淘首頁的分析的文章。
另外,本身也用rem佈局實踐過幾個項目。
不得不說,我的以爲rem佈局如今已經能夠放棄了。flex
佈局已經很好用了,早已有之的百分比佈局等稍用點心思也並不難。
這篇博客一直想改。但懶惰老是佔據着個人身體。
最後再說下,字體大小自適應是錯誤的,字體大小自適應是錯誤的,字體大小自適應是錯誤的。
rem 佈局,能夠告別了。
迎接 flex 佈局吧。
=========================================
這篇博客寫於半年前,那時候仍是個剛畢業的菜鳥。
偶爾有點想法,看了一些大牛的文章,有了這篇博客。
這也是半年來惟一一篇產出。
5k的瀏覽量,95收藏,13推薦,已經讓我很驚訝了。
謝謝各路大神們的關注。
半年來感覺到的前端大環境變化仍是很大。雖然在公司沒有太多變化,但眼睛總得看着世界吧。
接下來,還得繼續學習。
因爲手上沒什麼項目,一直想探索出一套本身的自動化流程,但到如今也只是積累了許多版的草稿。
nodejs
方面也得有所探索,nodejs
再加上 shelljs
和 yargs
用起來是真的很爽。(鳴謝阮大神的文章)
算是年終總結了。在前端的路上繼續走吧。
=========================================
媒體查詢和js動態計算是兩種方式。
首先,支持 CSS3 calc
方法 和 rem
、vw
單位的瀏覽器下,只須要html {font-size: 15.625vw;}
這樣一句就好,另外加個媒體查詢限制下。
以前的一大堆密集的斷點只是爲了hack不支持calc
或者calc
的狀況。其次,js動態設置html的font-size
,只要瀏覽器支持rem單位便可。
爲何會考慮到密集的mq斷點呢,由於當時還在考慮文字大小的自適應問題。
實踐證實,字體大小自適應是一種錯誤的想法。
移動開發在必要狀況的下,能夠適當使用mq來調整字體大小,但作成徹底自適應則是一種存在問題的作法。
所以,這裏提到的 calc
和vh
rem
配合的作法,最好只用來作佈局的工做。js動態計算也是相似,更適合作佈局。
Articles on responsive font: