作移動端自適應時可能不少人都對自適應和dpr之間的關係產生疑問,也有一些人會疑慮好比個人自適應方案沒有加dpr會不會出問題,針對這些疑問我說一下個人看法,但願能解除你的擔心。css
首先說到自適應,可能在不一樣人眼裏理解不一樣,特別與響應式的關係,在這裏說一下我所理解的自適應,和其與響應式的區別。先說響應式設計,響應式設計表示在不一樣的屏幕尺寸下,都有良好的佈局和內容表現,簡單一點的說,就是一個頁面能夠適配多種不一樣尺寸的屏幕,並且看上去仍是設計良好的。爲了實現這個目的,可能會利用js或者css去動態改變佈局的尺寸,在這個過程當中會伴隨元素尺寸的改變,佈局的改變,甚至會把元素隱藏,好比在pc端顯示的頁面轉到移動端就會這樣。而自適應每每考慮的是另外一個方面,就是但願頁面的設計與設計稿的設計比例一致,這個也是作自適應的目的,在這個過程當中針對不一樣的屏幕寬度元素的尺寸也會改變,可是通常不會有佈局改變,和元素的隱藏,由於設計稿就這樣,咱們得按設計師妹子的尺寸來寫頁面。因此按照我以上的說法,那些按照css媒體查詢寫的自適應嚴格來講不叫自適應,由於斷點之間會形成比例偏差,而讓偏差少一點就得多插值。很明顯使用css媒體查詢並非作自適應的好方法,咱們須要一種準確的方法來作這個事,這個時候js就出來了,下面將會列舉坊間流傳甚廣的淘寶方案和網易方案。html
點這裏能夠看到淘寶方案具體的代碼flexible前端
固然具體的代碼是作了不少的邊界處理和兼容處理的,可是核心能夠濃縮爲如下代碼android
(function () {
var dpr = window.devicePixelRatio;
var meta = document.createElement('meta');
var scale = 1 / dpr;
meta.setAttribute('name', 'viewport');
meta.setAttribute('content', 'width=device-width, user-scalable=no, initial-scale=' + scale +
', maximum-scale=' + scale + ', minimum-scale=' + scale);
document.getElementsByTagName('head')[0].appendChild(meta);
// 動態設置的縮放大小會影響佈局視口的尺寸
function resize() {
var deviceWidth = document.documentElement.clientWidth;
document.documentElement.style.fontSize = (deviceWidth / 10) +'px';
}
resize();
window.onresize = resize;
})()
複製代碼
這段代碼放在瀏覽器上就能作到自適應了,他的過程是先獲取設備的dpr,所謂的dpr就是設備像素比,什麼是設備像素比呢,就是單位尺寸內,設備物理像素的個數除以設備獨立像素的大小,物理像素就是手機屏幕上一個一個的發光的點,大小是固定的,獨立像素也叫作邏輯像素,css設置的像素大小就是邏輯像素,對於dpr等於2的手機屏幕,設置css寬度爲1px,其實覆蓋的是2個設備物理像素。回到正題,拿到dpr後,經過動態設置meta的viewport值,進行對佈局的縮放操做。這裏有一個關鍵,就是設置 width=device-width和initial-scale的大小,在描述二者的做用以前咱們先要理解一個概念就是佈局視口,佈局視口在以前有一個別名叫作初始包含塊,而在比較早的文獻中初始包含塊也叫作畫布。理解畫布可能比理解佈局視口更簡單,若是你按比例繪圖,不少時候就要參照你所用畫布的大小,好比設計師在750px畫了一個200px的正方形,若是你要在一張大小是100cm的紙上畫,你可能就要這樣計算正方形的寬度了 100cm * 200 / 750,能夠看到這個計算中是沒有用到dpr,你的筆觸跨過多少個紙張分子,多少個原子根本就不影響個人繪圖比例。咱們的畫畫的過程就至關於設置css的過程,css的尺寸依賴的就是佈局視口的大小,而網頁的佈局視口大小在標準模式下能夠這樣獲取git
document.documentElement.clientWidth,github
而兩個關鍵的元素設置 width=device-width,initial-scale = scale,作的事情就是先把佈局視口放大dpr倍,而後總體縮放相應倍數以適應設備尺寸。這個也很容易驗證在控制檯打印佈局視口大小就好了web
var meta = document.createElement('meta');
var scale = 0.1;
meta.setAttribute('name', 'viewport');
複製代碼
var meta = document.createElement('meta');
var scale = 0.5;
meta.setAttribute('name', 'viewport');
複製代碼
這裏能夠看到就算我設置scale不等於 1 / drp 的大小也不妨礙我按設計圖的比例畫出元素 這裏要注意兩點,由於我是用chrome模擬的,設置的時候發現幾個問題chrome
要作到自適應關鍵是讓元素的尺寸與佈局視口綁定關係,在這裏雖然佈局視口放大了,但並不影響這種綁定關係,這裏淘寶方案把佈局視口的寬度分割了十等份,每份的大小至關於佈局寬度的十分之一,而把每份的大小分配給根元素的字體大小,元素尺寸就能夠設置rem單位來與佈局視口綁定關係,以200px尺寸爲例,他們比例映射是這樣的瀏覽器
200px : 640px => xrem : 10rembash
這裏的10rem就是佈局視口寬度,元素尺寸只要維持這個比例關係就好了,與dpr是沒有關係的
x= 10 * 200 / 640 = 3.125rem
這裏的計算可能會費一點時間,也有一些插件能夠輔助把px轉爲rem的 可是方案是死的,人是活的,你只要把淘寶固有的十等分改一下就好了,好比設計稿是640px的 改一下
document.documentElement.style.fontSize = (deviceWidth / 6.4) +'px';
複製代碼
分了6.4等份
200px : 640px => xrem : 6.4rem
x一看就知道是 2rem
流程: rem => 根元素字體大小 => 佈局視口
固然也能夠分割爲100等份,這樣方便爲之後使用vw單位進行過渡
那麼爲何淘寶要引入dpr,把佈局放大再縮小呢,其中一點就是這個方案能夠很好地解決1px邊框的問題,對於高清屏來講設置1px像素大小,其實橫跨的是dpr個設備像素,這樣看起來線條不夠細,與設計稿就產生出入,而經過佈局放大再縮小的方案恰好就彌補了這個問題。可是隨之而來也帶來一個問題,看上面的截圖咱們看到字體大小發生了改變,在scale設置爲0.1時基本就看不見了,緣由是通常咱們的字體大小的設置不會使用rem,而是使用px單位,這裏的字體大小沒有隨佈局視口的放大而增大,卻隨頁面的總體縮放而縮小了,這裏就得要針對不一樣的dpr作響應的處理,在淘寶的代碼中咱們能夠看到
docEl.setAttribute('data-dpr', dpr);
複製代碼
就是經過在根元素上掛載dpr信息,而後設置相應的css屬性例如
[data-dpr=2] div{
font-size: 32px
}
[data-dpr=3] div{
font-size: 48px
}
複製代碼
特別對於安卓手機,各類神奇的dpr,若是每一個都這樣設置將會是災難 因此淘寶很是聰明
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;
複製代碼
夠簡單直接,安卓高清屏是不存在的, 可是其實影響也不大,就是安卓屏的1px線條粗一點而已
若是除了要作自適應還要作響應式,那也得像上面設置字體同樣一個一個設置,由於css媒體查詢也是針對佈局視口尺寸的。對於淘寶他們來講,確定有一套工程化的方案來解決這種技術難題,對於遇到這個坑的夥伴估計得自已想辦法了,預處理器是必不可少的。 從前面能夠知道淘寶引入dpr並非爲了作自適應的,而是爲了解決1px問題的,固然也引入了其餘難題,既然如此,放棄解決1px問題,不就簡單得多,網易方案就是這麼作的。
去除了邊界處理和兼容處理,因爲沒有動態設置meta因此要在head中引入
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
複製代碼
(function () {
var dpr = window.devicePixelRatio;
function resize() {
var deviceWidth = document.documentElement.clientWidth;
document.documentElement.style.fontSize = (deviceWidth / 6.4) +'px';
}
resize();
window.onresize = resize;
})()
複製代碼
網易方案沒有引入dpr相關的,這也說明了移動端自適應與dpr是無關的
200px : 640px => xrem : 6.4rem
x= 2rem
流程: rem => 根元素的大小 => 佈局視口
既然自適應與dpr無關那麼就能夠擴展出不少方案了
1.在佈局視口等於設備寬度時,直接把根元素字體大小綁定到設備寬度大小上
document.documentElement.style.fontSize = (screen.width/ 6.4) +'px';
複製代碼
這裏有關相關的文章 基於screen.width的僞響應式開發
2.直接定死佈局視口
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="viewport" content="width=640, user-scalable=no">//定死爲設計稿的尺寸
<meta name="renderer" content="webkit">
<title>定死佈局視口</title>
<style>
html, body {
margin: 0
}
div {
width: 200px;
height: 200px;
background: red;
}
body {
background: blue;
}
</style>
<script>
</script>
</head>
<body>
<div id="size200px">元素大小200px</div>
</body>
</html>
複製代碼
不用rem單位,不用設置js,可是佈局視口定死後,就不能用css媒體查詢作響應式了,從這裏也能夠看出viewport屬性的做用,就是讓佈局視口經過縮放來適配屏幕寬度,width=device.width僅僅是讓佈局視口初始大小等於設備寬度,後面設置的initial-scale是用來縮放佈局視口大小,並且默認是佈局視口初始大小等於設備寬度,也就是所謂的理想視口,換個說法就是若是你設置了initial-scale你能夠不用設置 width=device.width了,淘寶方案你把width=device.width去掉,並不會影響自適應過程,加上主要是防止一些不按規範的瀏覽器出現兼容問題。若是還不能理解viewport的做用,那麼能夠參考svg中的viewport和viewBox的關係,原理是同樣的。
3.使用新出單位 vw, vw 就是專門爲自適應而出現的,100vw就是佈局視口的寬度,很是厲害,你也不用設置js了
200px: 640px => xvw : 100vw
x=200 * 100 / 640 = 31.25vw
流程: vw => 佈局視口
看一下兼容性
兼容還能夠,這裏也有相關的資料 分享手淘過年項目中採用到的前端技術
移動端尺寸自適應與dpr無關,除了淘寶方案外,其餘方案都得處理1px的問題,但也減小針對不一樣dpr設備作響應式處理的麻煩,並且其中也沒有一種一勞永逸的方案能解決所有問題。而做爲新出來的單位vw,是時候該入坑了
1. 張鑫旭 [基於screen.width的僞響應式開發](https://www.zhangxinxu.com/wordpress/2016/06/pseudo-response-layout-base-on-screen-width/)
2. 大漠 [分享手淘過年項目中採用到的前端技術](https://www.w3cplus.com/css/taobao-2018-year.html)
3. [fleible](https://github.com/huainanhai/flexible/blob/master/public/js/frame/flexible.debug.js)
4. 張鑫旭 [設備像素比devicePixelRatio簡單介紹](設備像素比devicePixelRatio簡單介紹)複製代碼