響應式佈局方案
說到響應式佈局方案,我們首先需要了解視口這個概念
視口
在早期的時候我們沒有專門針對手機尺寸寫的頁面,所以在用手機瀏覽頁面的時候我們看到的都是專門針對PC端的頁面,在這種情況下頁面會被嚴重壓縮,而且會極大的影響頁面的結構和佈局,為了解決這個 問題,蘋果公司提出了視口的概念,因為我們早期的PC端的頁面的版心一般是960px,為了容納這個頁面,我們在手機建立一個虛擬的區域,大小一般為980px,來容納PC的頁面,以方便使用者來瀏覽頁面
但是在如今移動端快速發展的時候,我們有了專門針對手機螢幕尺寸的的移動端頁面,所以在寫移動端的頁面的時候我們需要調整視口的寬度,保證頁面內容會在手機螢幕尺寸大小的頁面上進行展示
<!--告訴瀏覽器如何調整裝置的視口大小--> <meta name="viewport" content="width=device-width ,initial-scale=1.0,user-scalable=no,maximun-scale=1.0,minimum-scale=1.0"> <meta name="viewport"> <!--告訴瀏覽器根據當前裝置的尺寸調整視口的大小--> <meta name="viewport" content="width=device-width"> <!--初始縮放比例為1 ,該宣告和width是同樣的效果,但是為了保證所有的瀏覽器都能相容,所以我們都會寫--> <meta name="viewport" content="initial-scale=1.0"> <!--設定使用者不可以對頁面大小進行縮放--> <meta name="viewport" content="user-scalbale-no"> <meta name="viewport" content="maximum-scale=1.0,minimum-scale=1.0">
媒體查詢
媒體型別
媒體查詢可以為不同的裝置規定不同的樣式,常用的一般有三種裝置 screen(計算機螢幕,該值是預設值)、print(列印預覽)、all(所有裝置)
@media screen
媒體屬性
媒體屬性用來規定在指定的符合某些條件的情況下為指定的元素設定樣式,需要注意媒體屬性必須用 ()
包起來,否則無效,常用的媒體屬性有
width 可視區域寬度,取值格式為指定寬度或者範圍,如 @media (width: 900px){ }
height 可視區域高度,取值格式為指定寬度或者範圍,如 @media (max-height: 900px){ }
device-width 裝置寬度,取值格式為指定寬度或者範圍,如 @media (max-device-width: 5000px) { }
device-height 裝置高度 取值格式為指定寬度或者範圍,如 @media (max-device-height: 5000px) { }
orientation 裝置是豎屏還是橫屏模式,可選值有landscape(橫屏)、portrait(豎屏),如 @media (orientation: landscape) { }
aspect-ratio 可視區域寬高比,取值格式為水平畫素/垂直畫素,如@media (device-aspect-ratio:16/9) { }
device-aspect-ratio 裝置寬高比,取值格式為水平畫素/垂直畫素,如@media (device-aspect-ratio:16/9) { }
例如我們要針對某個裝置的 body
在橫屏時設定的背景顏色是黑色,在豎屏的時候背景是白色,那麼可以使用以下寫法
html, body { height: 100%; } @media(orientation: landscape) { body{ background-color:#000; } } @media(orientation: portrait) { body{ background-color:#fff; } }
邏輯操作符
媒體查詢可以使用三種操作符,通過操作符配合媒體屬性來判斷是否載入媒體屬性下的樣式表
& ||
配合邏輯操作符設定裝置在可視視窗大小變化時背景顏色發生改變
html,body{ height: 100%; } @media screen and (max-width: 500px ){<!--在螢幕尺寸小於500px時body背景顏色為skyblue--> body{ background-color: skyblue; } } @media screen and (min-width: 501px) and (max-width: 800px){<!--在螢幕尺寸為501-800px時,背景顏色為灰色--> body{ background-color: #ccc; } } @media screen and (min-width: 900px){<!--在螢幕尺寸為900px以上時,body背景顏色為yellowgreen--> body{ background-color: yellowgreen; } }
vw、vh佈局方案
在CSS3規範中引入了 vw
、 vh
單位,分別將視口劃分為100份,一個vw單位相當於視口寬度的1%,一個vh相當於視口高度的1%,需要注意的是不同於百分比的佈局方案,vw和vh不受父元素寬高的影響,只由視口的大小決定
同時還有兩個單位 vmax
、 vmin
, vmax
表示取視口寬度和高度中比較大的值,將其等分為100份, vmin
表示取比較小的值,將其等分為100份
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <style> .box{ width: 100vmin; height: 100vmax; background-color:deepskyblue; } .box div{ width: 50vh; height: 50vw; background-color: mediumseagreen; } </style> <title>vw、vh</title> </head> <body> <div class="box"> <div/> </div> </body> </html>
目前瀏覽器的支援情況
rem佈局
rem是css中的一個單位,是根據根字型大小來設定的,也就是html的font-size
在正常的UI設計稿件的時候一般設定大小為640/750px大小,我們一般選擇將稿件等分為20份(20份在裝置大小和稿件大小一般可以除盡,我們儘量避免出現小數)
那麼如果UI稿件為640px大小,每一份的大小為32px,假設UI稿件上有一個160*160大小的元素,那麼該元素佔整個頁面的160/32份
同時我們也將螢幕分為20份,以Iphon為例(螢幕大小為320px),那麼每份大小為16px,我們通過媒體查詢將html根字型大小設定為16px,那麼在UI稿件上160px大小的元素在Iphon5上的實際大小為160/32 rem,那麼通過這個公式我們就可以通過JavaScript設定不同的根節點字型大小來進行適配
我們來寫一個簡單的例子
(function (doc, win) { var docEl = doc.documentElement, /* document.documentElement 返回文件的根節點 * orientationchange 在使用者移動端裝置螢幕垂直或水平旋轉移動裝置時被觸發 * resize 事件,在繫結元素大小發生變化時觸發該事件,例如檢測螢幕變化: * window.addEventLisenter("resize",function(){ *alert("螢幕大小變化了") * }) * * resize 屬性,css3新增屬性,用來指定使用者是否可以縮放該元素 * none:使用者無法調整該元素尺寸 * both:使用者可以調整元素的高度和寬度 * horizontal:使用者可調整元素的寬度 * vertical:使用者可調整元素的寬度 * */ resizeEvt = "orientationchange" in window ? "orientationchange" : "resize", recalc = function () { var clientWidth = docEl.clientWidth; if (clientWidth >= 640) { clientWidth = 640 } if (!clientWidth) return; docEl.body.style.fontSize = (clientWidth / 640) * 100 + "px"; }; win.addEventListener("resizeEvt", recalc, false); /* * DOMContentLoaded 該事件會在load事件之前觸發,在DOM樹構建完成時就觸發 * 而load事件則是在DOMContentLoaded事件觸發之後,繼續載入圖片等外部檔案完成後觸發 * */ doc.addEventListener("DOMContentLoaded", recalc, false) })(document, window)
之前一直在使用手淘的rem佈局方案,可以大概看一下原始碼
;(function(win, lib) { var doc = win.document; var docEl = doc.documentElement; var metaEl = doc.querySelector('meta[name="viewport"]'); var flexibleEl = doc.querySelector('meta[name="flexible"]'); var dpr = 0; var scale = 0; var tid; var flexible = lib.flexible || (lib.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); //1 } } 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)); } } } if (!dpr && !scale) { //檢測的當前裝置的版本 var isAndroid = win.navigator.appVersion.match(/android/gi); var isIPhone = win.navigator.appVersion.match(/iphone/gi); //window.devicePixelRatio 該屬性返回當前顯示裝置的物理畫素解析度與css畫素解析度的比值,該值也可以被解釋為畫素大小的比例 //簡單來說就是一個css畫素的大象相對於一個物理畫素大小的比值 //可以通過重寫window.devicePixelRatio來更改此屬性,例如window.devicePixelRatio=2; //dpr css/物理 比例 //scale 縮放比例 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; } docEl.setAttribute('data-dpr', dpr); //判斷頁面是否存在 metaEl 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); } } function refreshRem(){ var width = docEl.getBoundingClientRect().width; //getBoundingClientRect //返回一個DOMRect物件,包含一組矩形的集合,該集合內是與該元素相關的css邊框集合 /* * DOMRect * bottom:8 * height:8 * left:0 * right:520 * top:0 * width:520 * x:0 * y:0 * */ if (width / dpr > 540) { width = 540 * dpr; } //阿里的佈局方案預設將螢幕分成十份,當然,如果願意我們可以對其進行更改 var rem = width / 10; docEl.style.fontSize = rem + 'px'; flexible.rem = win.rem = rem; } // resize 在裝置寬度發生改變時觸發 win.addEventListener('resize', function() { clearTimeout(tid); tid = setTimeout(refreshRem, 300); }, false); //pageshow firefox/open的一個事件,在chrome中不會觸發 //在頁面後退時靜態資源會直接重快取中讀取 win.addEventListener('pageshow', function(e) { if (e.persisted) { clearTimeout(tid); tid = setTimeout(refreshRem, 300); } }, false); //document.readyState 描述文件的載入狀態 /* * loding 文件仍然在載入中 * interactive 文件已經載入完成並已經被解析,但是影象,框架之類的資源仍然在載入中 * complete 說有資源都已經載入完成,load事件即將被觸發 * 在狀態改變時document.readyState事件將被觸發 * */ if (doc.readyState === 'complete') { doc.body.style.fontSize = 12 * dpr + 'px'; } else { //DOMContentLoaded 在文件載入完成後觸發,不會等待影象,框架等資源,參考$(function(){}) / $.ready() doc.addEventListener('DOMContentLoaded', function(e) { doc.body.style.fontSize = 12 * dpr + 'px'; }, false); } refreshRem(); flexible.dpr = win.dpr = dpr; flexible.refreshRem = refreshRem; //rem 2 px轉化方法 flexible.rem2px = function(d) { var val = parseFloat(d) * this.rem; if (typeof d === 'string' && d.match(/rem$/)) { val += 'px'; } return val; } //px 2 rem轉化方法 flexible.px2rem = function(d) { var val = parseFloat(d) / this.rem; if (typeof d === 'string' && d.match(/px$/)) { val += 'rem'; } return val; } })(window, window['lib'] || (window['lib'] = {}));
阿里的佈局方法主要是通過dpr來設定不同螢幕下的不同比例關係,具體的關於dpr的解釋可以看下面這篇文章