一,flexible.js 的使用方式:
github地址:https://github.com/amfe/lib-flexible
官方文檔地址:https://github.com/amfe/article/issues/17
本文中有部分內(nèi)容引至上面這個(gè)文檔织咧。
(一)哄啄,引用方式
1,引用cdn地址
<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.2/??flexible_css.js,flexible.js"
></script>
當(dāng)前最新的版本是0.3.2懈息。
2肾档,下載flexible.js 等文件到項(xiàng)目指定目錄下,然后在head中引入辫继。建議對(duì)于js做內(nèi)聯(lián)處理怒见,在所有資源加載之前執(zhí)行這個(gè)js。
下面是淘寶的寫(xiě)法:
<!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原理
在頁(yè)面中引入flexible.js后遣耍,flexible會(huì)在<html>標(biāo)簽上增加一個(gè)data-dpr屬性和font-size樣式(如下圖)。
js首先會(huì)獲取設(shè)備型號(hào)炮车,然后根據(jù)不同設(shè)備添加不同的data-dpr值舵变,比如說(shuō)1酣溃、2或者3,從源碼中我們可以看到纪隙。
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下赊豌,對(duì)于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 {
// 其他設(shè)備下碘饼,仍舊使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
}
另外,頁(yè)面中的元素用rem單位來(lái)設(shè)置悲伶,rem就是相對(duì)于根元素<html>的font-size來(lái)計(jì)算的艾恼,flexible.js能根據(jù)<html>的font-size計(jì)算出元素的盒模型大小。這樣就意味著我們只需要在根元素確定一個(gè)px字號(hào)麸锉,因此來(lái)算出各元素的寬高钠绍,從而實(shí)現(xiàn)屏幕的適配效果。
(三)淮椰,把視覺(jué)稿中的px轉(zhuǎn)換成rem
工作中我們常見(jiàn)的視覺(jué)稿大小大至可為640五慈、750、1125三種主穗。不過(guò)flexible.js并沒(méi)有限制只能用這三種泻拦,所以你還可以根據(jù)自身情況來(lái)調(diào)整,具體如何轉(zhuǎn)換忽媒,我們以視覺(jué)稿為640px的寬來(lái)舉例子争拐,把640px分為100份,每一份稱(chēng)為一個(gè)單位a晦雨,那么每個(gè)a就是6.4px架曹,而1rem單位被認(rèn)定為10a,此時(shí)闹瞧,1rem=1(a)X10X6.4(px)即64px绑雄。
640px/100=6.4px 1個(gè)單位a為6.4px
1rem = 10a 1rem單位被認(rèn)定為10a
1rem = 1(a)*10*6.4(px) = 64px
因此,對(duì)于視覺(jué)稿上的元素的尺寸換算奥邮,只需要原始px值除以rem基準(zhǔn)px值(此例子中為64px)即可万牺。例如240px * 120px的元素,最后轉(zhuǎn)換為3.75rem * 1.875rem洽腺。
在開(kāi)發(fā)過(guò)程中那我們?nèi)绾慰焖俚陌裵x轉(zhuǎn)換成rem呢脚粟?
1,如果你用的是Sublime Text3蘸朋,你可以直接在這個(gè)編輯器上安裝CSSREM插件核无。
github地址:https://github.com/flashlizi/cssrem
2,如果你用的是其他編輯器或者IDE藕坯,就可以用CSS的處理器來(lái)處理团南,比如說(shuō)Sass噪沙、LESS以及PostCSS這樣的處理器。我們簡(jiǎn)單來(lái)看兩個(gè)示例已慢。
@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函數(shù)外曲聂,還可以使用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;
}
}
(四),字體不使用rem的方法
工作中做完一個(gè)觸屏版的頁(yè)面后佑惠,我們會(huì)拿iPhone5s朋腋、iPhone6、iPhone6s等手機(jī)進(jìn)行測(cè)試膜楷,他們都是Retina屏旭咽,我們當(dāng)然希望在這些手機(jī)型號(hào)上看到的文本字號(hào)是相同的。也就是說(shuō)赌厅,我們不希望文本在Retina屏幕下變小穷绵,另外,我們希望在大屏手機(jī)上看到更多文本(例如iPhone7特愿、iPhone7Plus)仲墨。另外,現(xiàn)在絕大多數(shù)的字體文件都自帶一些點(diǎn)陣尺寸揍障,通常是16px和24px目养,都是偶數(shù),所以我們不希望出現(xiàn)13px和15px這樣的奇葩尺寸毒嫡。
如此一來(lái)癌蚁,就決定了在制作H5的頁(yè)面中,rem并不適合用到段落文本上兜畸。所以在Flexible整個(gè)適配方案中努释,考慮文本還是使用px作為單位。只不過(guò)使用[data-dpr]屬性來(lái)區(qū)分不同dpr下的文本字號(hào)大小咬摇。
div {
width: 1rem;
height: 0.4rem;
font-size: 12px; // 默認(rèn)寫(xiě)上dpr為1的fontSize
}
[data-dpr="2"] div {
font-size: 24px;
}
[data-dpr="3"] div {
font-size: 36px;
}
為了能更好的利于開(kāi)發(fā)伐蒂,在實(shí)際開(kāi)發(fā)中,我們可以定制一個(gè)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;
}
}
有了這樣的混合宏之后肛鹏,在開(kāi)發(fā)中可以直接這樣使用:
@include font-dpr(16px);
當(dāng)然這只是針對(duì)于描述性的文本饿自,比如說(shuō)段落文本。但有的時(shí)候文本的字號(hào)也需要分場(chǎng)景的,比如在項(xiàng)目中有一個(gè)slogan复唤,業(yè)務(wù)方希望這個(gè)slogan能根據(jù)不同的終端適配妓局。針對(duì)這樣的場(chǎng)景,完全可以使用rem給slogan做計(jì)量單位好爬。
(五)局雄,viewport的meta標(biāo)簽。
該標(biāo)簽主要用來(lái)告訴瀏覽器如何規(guī)范的渲染W(wǎng)eb頁(yè)面存炮,而你則需要告訴它視窗有多大炬搭。在開(kāi)發(fā)移動(dòng)端頁(yè)面,我們需要設(shè)置meta標(biāo)簽如下:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
代碼以顯示網(wǎng)頁(yè)的屏幕寬度定義了視窗寬度穆桂。網(wǎng)頁(yè)的比例和最大比例被設(shè)置為100%宫盔。
而我們?cè)谑褂胒lexible.js時(shí)候就只需要像下面這樣寫(xiě)<meta>標(biāo)簽,或者干脆省略下面的標(biāo)簽:
<meta name="viewport" content="width=device-width, user-scalable=no">
或者我們也可以像flexible的github例子中那樣寫(xiě):
<meta content="maximum-dpr=2" name="flexible" />
原理:flexible.js會(huì)先去獲取頁(yè)面上[name="viewport"]或[name="flexible"]的meta標(biāo)簽享完,如果有就直接根據(jù)獲取到的值來(lái)判斷灼芭,如果沒(méi)有,會(huì)自己創(chuàng)建一個(gè)meta標(biāo)簽般又,我們看一下源碼就知道了彼绷。
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>標(biāo)簽之后,就可以動(dòng)態(tài)改寫(xiě)data-dpr和font-size兩個(gè)屬性的值茴迁,因此也就達(dá)到了適配的效果寄悯。
二,手動(dòng)設(shè)置的相關(guān)問(wèn)題:
(一)手動(dòng)配置dpr
引入執(zhí)行js之前笋熬,可以通過(guò)輸出meta標(biāo)簽方式來(lái)手動(dòng)設(shè)置dpr热某。語(yǔ)法如下:
<meta name="flexible" content="initial-dpr=2, maximum-dpr=3" />
注意:initial-dpr=2, maximum-dpr=3這兩個(gè)參數(shù)只能選其一。
(二)胳螟,當(dāng)我們手動(dòng)設(shè)置maximum-dpr=x時(shí)
在flexible的github例子中昔馋,添加maximum-dpr這個(gè)屬性,content="maximum-dpr=2"糖耸,這個(gè)屬性是給出一個(gè)最大的dpr限制秘遏,然后比較系統(tǒng)的dpr和給定的dpr,取最小值嘉竟。
(三)邦危,當(dāng)我們手動(dòng)設(shè)置initial-dpr=x時(shí)
如果要使用flexible.js來(lái)做布局的話(huà),建議不要添加這個(gè)屬性舍扰,因?yàn)檫@個(gè)屬性會(huì)把dpr強(qiáng)制設(shè)置為給定的值倦蚪,如果手動(dòng)設(shè)置initial-dpr=1之后,不管設(shè)備是多少dpr都會(huì)強(qiáng)制認(rèn)為其dpr是你設(shè)備的值边苹。
另外陵且,在flexible中,只對(duì)IOS設(shè)備進(jìn)行dpr判斷个束,對(duì)于Android系列始終認(rèn)為其dpr為1慕购,手機(jī)淘寶并沒(méi)有對(duì)安卓的dpr進(jìn)行一個(gè)適配聊疲。咱們可以通過(guò)flexible.js的源碼來(lái)看:
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下,對(duì)于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 {
// 其他設(shè)備下,仍舊使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
}
作者:Scaukk 在這篇http://www.reibang.com/p/221bebfae266文章中是這么理解的:
android手機(jī)屏幕大小殿如,寬高比是花開(kāi)滿(mǎn)地贡珊,要做的調(diào)整真的是太多了。如果根元素的font-size尺寸不對(duì)握截,頁(yè)面效果不用多說(shuō)飞崖。
就算把當(dāng)前的設(shè)備信息都考慮進(jìn)去了,那以后呢谨胞。
所以固歪,考慮開(kāi)發(fā),維護(hù)胯努,兼容性...淘寶這么做還是有道理的牢裳。
(四),手動(dòng)設(shè)置rem基準(zhǔn)值的方法:
html{ font-size: 60px !important; }
三叶沛,需要注意的幾個(gè)地方:
(一)蒲讯,遇到下面兩種情況的時(shí)候,我們?cè)谇许?yè)面的時(shí)候需要切兩套圖片,即@2x和@3x:
1,當(dāng)圖標(biāo)被放大時(shí)會(huì)模糊净宵。
2,當(dāng)產(chǎn)品對(duì)頁(yè)面上的圖片清晰度要求很高時(shí)晦墙。
@2x為750X1334的設(shè)計(jì)稿(高度會(huì)隨著內(nèi)容多少而改變)。@3x為1125X2001的設(shè)計(jì)稿(高度會(huì)隨著內(nèi)容多少而改變)肴茄。如果要放大設(shè)計(jì)稿來(lái)切圖的時(shí)候是等比放大1.5倍晌畅。
(二), 解決雪碧圖的問(wèn)題寡痰,建議能用SVG的地方就盡量用SVG抗楔,或者有些常用的圖標(biāo)用iconfont來(lái)替代,另外拦坠,有些小圖片在遇到dpr=2時(shí)连躏,可能會(huì)模糊,這時(shí)建議用大圖來(lái)切圖贞滨。
五反粥,幾個(gè)后期我們開(kāi)發(fā)中可能會(huì)遇到的名詞:
Element.getBoundingClientRect().width 用來(lái)獲取元素自身的寬度。
Element.getBoundingClientRect()用來(lái)獲取頁(yè)面中某個(gè)元素的左、上才顿、右、下分別相對(duì)于瀏覽器視窗的位置尤蒿,是DOM元素到瀏覽器可視范圍的距離(不含頁(yè)面不可見(jiàn)部分)郑气。
設(shè)備的px不會(huì)改變,css的px改變%(百分比)時(shí)腰池,不會(huì)影響設(shè)備的px尾组,只是原本設(shè)備的1個(gè)px中可能會(huì)顯示多個(gè)或不足一個(gè)css的px。當(dāng)縮放級(jí)別100%時(shí)示弓,1個(gè)單位的css px嚴(yán)格等于1個(gè)單位的設(shè)備px讳侨。
screen.width、screen.height用戶(hù)屏幕的完整寬度和高度奏属。
window.innerWidth跨跨、window.innerHeight瀏覽器窗口內(nèi)部寬度和高度的尺寸,包含了滾動(dòng)條的尺寸囱皿。
window.pageXOffset勇婴、window.pageYOffset用戶(hù)滾動(dòng)了多少滾動(dòng)條的距離。
視窗viewport 簡(jiǎn)單的理解嘱腥,viewport是嚴(yán)格等于瀏覽器的窗口耕渴。在桌面瀏覽器中,viewport就是瀏覽器窗口的寬度高度齿兔。但在移動(dòng)端設(shè)備上就有點(diǎn)復(fù)雜橱脸。移動(dòng)端的viewport太窄,為了能更好為CSS布局服務(wù)分苇,所以提供了兩個(gè)viewport:虛擬的viewportvisualviewport和布局的viewportlayoutviewport添诉。
Retina 是視網(wǎng)膜的意思,指顯示屏的分辨率極高组砚,使得肉眼無(wú)法分辨單個(gè)像素吻商。
物理像素,也可以稱(chēng)為設(shè)備像素糟红,他是顯示設(shè)備中一個(gè)最微小的物理部件艾帐,每個(gè)像素可以根據(jù)操作系統(tǒng)設(shè)置自己的顏色和亮度。正是這些設(shè)備像素的微小距離欺騙了我們?nèi)庋劭吹降膱D像效果盆偿。
設(shè)備獨(dú)立像素也稱(chēng)為密度無(wú)關(guān)像素柒爸,可以認(rèn)為是計(jì)算機(jī)坐標(biāo)系統(tǒng)中的一個(gè)點(diǎn),這個(gè)點(diǎn)代表一個(gè)可以由程序使用的虛擬像素(比如說(shuō)CSS像素)事扭,然后由相關(guān)系統(tǒng)轉(zhuǎn)換為物理像素捎稚。
CSS像素是一個(gè)抽像的單位,主要使用在瀏覽器上,用來(lái)精確度量Web頁(yè)面上的內(nèi)容今野。一般情況之下葡公,CSS像素稱(chēng)為與設(shè)備無(wú)關(guān)的像素(device-independent pixel),簡(jiǎn)稱(chēng)DIPs条霜。
屏幕密度催什,即設(shè)備表面上存在的像素?cái)?shù)量,通常以每英寸有多少像素來(lái)計(jì)算(PPI)宰睡。
設(shè)備像素比(device pixel ratio)蒲凶,簡(jiǎn)稱(chēng)dpr,定義了物理像素和設(shè)備獨(dú)立像素的對(duì)應(yīng)關(guān)系拆内,它的值可以按下面的公式計(jì)算得到:
設(shè)備像素比 = 物理像素 / 設(shè)備獨(dú)立像素
眾所周知旋圆,iPhone6的設(shè)備寬度和高度為375pt * 667pt,可以理解為設(shè)備的獨(dú)立像素;而其dpr為2麸恍,根據(jù)上面公式灵巧,我們可以很輕松得知其物理像素為750pt * 1334pt。
在不同的屏幕上或南,CSS像素所呈現(xiàn)的物理尺寸是一致的孩等,而不同的是CSS像素所對(duì)應(yīng)的物理像素具數(shù)是不一致的。
在普通屏幕下1個(gè)CSS像素對(duì)應(yīng)1個(gè)物理像素采够,而在Retina屏幕下肄方,1個(gè)CSS像素對(duì)應(yīng)的卻是4個(gè)物理像素。