github地址:https://github.com/amfe/lib-flexible
官方文檔地址:https://github.com/amfe/article/issues/17
本文中有部份內容引至上面這個文檔。javascript
<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.2/??flexible_css.js,flexible.js" ></script>
當前最新的版本是0.3.2。php
下面是淘寶的寫法:css
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8" /> <meta content="yes" name="apple-mobile-web-app-capable" /> <meta content="yes" name="apple-touch-fullscreen" /> <meta content="telephone=no,email=no" name="format-detection" /> <meta content="maximum-dpr=2" name="flexible" /> <script src="build/flexible_css.js"></script> <script src="build/flexible.js"></script> <title>lib.flexible</title> </head>
在頁面中引入flexible.js後,flexible會在<html>標籤上增長一個data-dpr屬性和font-size樣式(以下圖)。html
js首先會獲取設備型號,而後根據不一樣設備添加不一樣的data-dpr值,好比說一、2或者3,從源碼中咱們能夠看到。java
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; }
另外,頁面中的元素用rem單位來設置,rem就是相對於根元素<html>的font-size來計算的,flexible.js能根據<html>的font-size計算出元素的盒模型大小。這樣就意味着咱們只須要在根元素肯定一個px字號,所以來算出各元素的寬高,從而實現屏幕的適配效果。android
工做中咱們常見的視覺稿大小大至可爲640、750、1125三種。不過flexible.js並無限制只能用這三種,因此你還能夠根據自身狀況來調整,具體如何轉換,咱們以視覺稿爲640px的寬來舉例子,把640px分爲100份,每一份稱爲一個單位a,那麼每一個a就是6.4px,而1rem單位被認定爲10a,此時,1rem=1(a)X10X6.4(px)即64px。git
640px/100=6.4px 1個單位a爲6.4px 1rem = 10a 1rem單位被認定爲10a 1rem = 1(a)*10*6.4(px) = 64px
所以,對於視覺稿上的元素的尺寸換算,只須要原始px值除以rem基準px值(此例子中爲64px)便可。例如240px * 120px的元素,最後轉換爲3.75rem * 1.875rem。github
github地址:https://github.com/flashlizi/cssremweb
@function px2em($px, $base-font-size: 75px) { @if (unitless($px)) { @warn "Assuming #{$px} to be in pixels, attempting to convert it into pixels for you"; @return px2em($px + 0px); // That may fail. } @else if (unit($px) == em) { @return $px; } @return ($px / $base-font-size) * 1em; }
除了使用Sass函數外,還可使用Sass的混合宏:瀏覽器
@mixin px2rem($property,$px-values,$baseline-px:75px,$support-for-ie:false){ //Conver the baseline into rems $baseline-rem: $baseline-px / 1rem * 1; //打印出第一行的像素值 @if $support-for-ie { #{$property}: $px-values; } //if there is only one (numeric) value, return the property/value line for it. @if type-of($px-values) == "number"{ #{$property}: $px-values / $baseline-rem; } @else { //Create an empty list that we can dump values into $rem-values:(); @each $value in $px-values{ // If the value is zero or not a number, return it @if $value == 0 or type-of($value) != "number"{ $rem-values: append($rem-values, $value / $baseline-rem); } } // Return the property and its list of converted values #{$property}: $rem-values; } }
工做中作完一個觸屏版的頁面後,咱們會拿iPhone5s、iPhone六、iPhone6s等手機進行測試,他們都是Retina屏,咱們固然但願在這些手機型號上看到的文本字號是相同的。也就是說,咱們不但願文本在Retina屏幕下變小,另外,咱們但願在大屏手機上看到更多文本(例如iPhone七、iPhone7Plus)。另外,如今絕大多數的字體文件都自帶一些點陣尺寸,一般是16px和24px,都是偶數,因此咱們不但願出現13px和15px這樣的奇葩尺寸。
如此一來,就決定了在製做H5的頁面中,rem並不適合用到段落文本上。因此在Flexible整個適配方案中,考慮文本仍是使用px做爲單位。只不過使用[data-dpr]屬性來區分不一樣dpr下的文本字號大小。
div {
width: 1rem; height: 0.4rem; font-size: 12px; // 默認寫上dpr爲1的fontSize } [data-dpr="2"] div { font-size: 24px; } [data-dpr="3"] div { font-size: 36px; }
爲了能更好的利於開發,在實際開發中,咱們能夠定製一個font-dpr()這樣的Sass混合宏:
@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; } }
有了這樣的混合宏以後,在開發中能夠直接這樣使用:
@include font-dpr(16px);
固然這只是針對於描述性的文本,好比說段落文本。但有的時候文本的字號也須要分場景的,好比在項目中有一個slogan,業務方但願這個slogan能根據不一樣的終端適配。針對這樣的場景,徹底可使用rem給slogan作計量單位。
該標籤主要用來告訴瀏覽器如何規範的渲染Web頁面,而你則須要告訴它視窗有多大。在開發移動端頁面,咱們須要設置meta標籤以下:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
代碼以顯示網頁的屏幕寬度定義了視窗寬度。網頁的比例和最大比例被設置爲100%。
<meta name="viewport" content="width=device-width, user-scalable=no">
或者咱們也能夠像flexible的github例子中那樣寫:
<meta content="maximum-dpr=2" name="flexible" />
原理:flexible.js會先去獲取頁面上[name="viewport"]或[name="flexible"]的meta標籤,若是有就直接根據獲取到的值來判斷,若是沒有,會本身建立一個meta標籤,咱們看一下源碼就知道了。
var metaEl = doc.querySelector('meta[name="viewport"]'); var flexibleEl = doc.querySelector('meta[name="flexible"]'); ... 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); } }
有了<meta>標籤以後,就能夠動態改寫data-dpr和font-size兩個屬性的值,所以也就達到了適配的效果。
引入執行js以前,能夠經過輸出meta標籤方式來手動設置dpr。語法以下:
<meta name="flexible" content="initial-dpr=2, maximum-dpr=3" />
注意:initial-dpr=2, maximum-dpr=3這兩個參數只能選其一。
在flexible的github例子中,添加maximum-dpr這個屬性,content="maximum-dpr=2",這個屬性是給出一個最大的dpr限制,而後比較系統的dpr和給定的dpr,取最小值。
若是要使用flexible.js來作佈局的話,建議不要添加這個屬性,由於這個屬性會把dpr強制設置爲給定的值,若是手動設置initial-dpr=1以後,無論設備是多少dpr都會強制認爲其dpr是你設備的值。
另外,在flexible中,只對IOS設備進行dpr判斷,對於Android系列始終認爲其dpr爲1,手機淘寶並無對安卓的dpr進行一個適配。我們能夠經過flexible.js的源碼來看:
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; }
做者:Scaukk 在這篇http://www.jianshu.com/p/221bebfae266文章中是這麼理解的:
android手機屏幕大小,寬高比是花開滿地,要作的調整真的是太多了。若是根元素的font-size尺寸不對,頁面效果不用多說。
就算把當前的設備信息都考慮進去了,那之後呢。
因此,考慮開發,維護,兼容性...淘寶這麼作仍是有道理的。
html{ font-size: 60px !important; }
@2x爲750X1334的設計稿(高度會隨着內容多少而改變)。@3x爲1125X2001的設計稿(高度會隨着內容多少而改變)。若是要放大設計稿來切圖的時候是等比放大1.5倍。
Element.getBoundingClientRect().width 用來獲取元素自身的寬度。
Element.getBoundingClientRect()用來獲取頁面中某個元素的左、上、右、下分別相對於瀏覽器視窗的位置,是DOM元素到瀏覽器可視範圍的距離(不含頁面不可見部分)。
設備的px不會改變,css的px改變%(百分比)時,不會影響設備的px,只是本來設備的1個px中可能會顯示多個或不足一個css的px。當縮放級別100%時,1個單位的css px嚴格等於1個單位的設備px。
screen.width、screen.height用戶屏幕的完整寬度和高度。
window.innerWidth、window.innerHeight瀏覽器窗口內部寬度和高度的尺寸,包含了滾動條的尺寸。
window.pageXOffset、window.pageYOffset用戶滾動了多少滾動條的距離。
視窗viewport 簡單的理解,viewport是嚴格等於瀏覽器的窗口。在桌面瀏覽器中,viewport就是瀏覽器窗口的寬度高度。但在移動端設備上就有點複雜。移動端的viewport太窄,爲了能更好爲CSS佈局服務,因此提供了兩個viewport:虛擬的viewportvisualviewport和佈局的viewportlayoutviewport。
Retina 是視網膜的意思,指顯示屏的分辨率極高,使得肉眼沒法分辨單個像素。
物理像素,也能夠稱爲設備像素,他是顯示設備中一個最微小的物理部件,每一個像素能夠根據操做系統設置本身的顏色和亮度。正是這些設備像素的微小距離欺騙了咱們肉眼看到的圖像效果。
設備獨立像素也稱爲密度無關像素,能夠認爲是計算機座標系統中的一個點,這個點表明一個能夠由程序使用的虛擬像素(好比說CSS像素),而後由相關係統轉換爲物理像素。
CSS像素是一個抽像的單位,主要使用在瀏覽器上,用來精確度量Web頁面上的內容。通常狀況之下,CSS像素稱爲與設備無關的像素(device-independent pixel),簡稱DIPs。
屏幕密度,即設備表面上存在的像素數量,一般以每英寸有多少像素來計算(PPI)。
設備像素比(device pixel ratio),簡稱dpr,定義了物理像素和設備獨立像素的對應關係,它的值能夠按下面的公式計算獲得:
設備像素比 = 物理像素 / 設備獨立像素
衆所周知,iPhone6的設備寬度和高度爲375pt * 667pt,能夠理解爲設備的獨立像素;而其dpr爲2,根據上面公式,咱們能夠很輕鬆得知其物理像素爲750pt * 1334pt。 在不一樣的屏幕上,CSS像素所呈現的物理尺寸是一致的,而不一樣的是CSS像素所對應的物理像素具數是不一致的。 在普通屏幕下1個CSS像素對應1個物理像素,而在Retina屏幕下,1個CSS像素對應的倒是4個物理像素。