前端響應式佈局原理與方案(詳細版)

引言

響應式佈局指的是同一頁面在不一樣屏幕尺寸下有不一樣的佈局。傳統的開發方式是PC端開發一套,手機端再開發一套,而使用響應式佈局只要開發一套就夠,缺點就是CSS比較重。下面是博客網站對不一樣設備適配後的結果,分別是iPhone5/SE,iphone6/7/8,iphone 6/7/8 plus,ipad pro,dell臺式寬屏(1440 X 900)javascript

響應式設計與自適應設計的區別:響應式開發一套界面,經過檢測視口分辨率,針對不一樣客戶端在客戶端作代碼處理,來展示不一樣的佈局和內容;自適應須要開發多套界面,經過檢測視口分辨率,來判斷當前訪問的設備是pc端、平板、手機,從而請求服務層,返回不一樣的頁面。css

響應式佈局的實現方案

1. 媒體查詢

CSS3媒體查詢可讓咱們針對不一樣的媒體類型定義不一樣的樣式,當重置瀏覽器窗口大小的過程當中,頁面也會根據瀏覽器的寬度和高度從新渲染頁面。html

如何選擇屏幕大小分割點

如何肯定媒體查詢的分割點也是一個開發中會遇到的問題,下面是市場上的移動設備和電腦屏幕分辨率的分佈狀況,能夠發現不一樣品牌和型號的設備屏幕分辨率通常都不同前端

img

若是咱們選擇600px,900px,1200px,1800px做爲分割點,能夠適配到常見的14個機型:java

img

固然這只是其中的一種分割方案,咱們還能夠這樣劃分:480px,800px,1400px,1400pxios

img

而做爲曾經典型的響應式佈局框架,Bootstrap是怎麼進行斷點的呢?css3

img

上面的分割方案不必定知足項目中的實際需求,咱們能夠先用跨度大的分割點進行分割,若是出現不適配的狀況能夠再根據實際狀況增長新的分割點。git

移動優先 OR PC優先

不論是移動優先仍是PC優先,都是依據當隨着屏幕寬度增大或減少的時候,後面的樣式會覆蓋前面的樣式。所以,移動端優先首先使用的是min-width,PC端優先使用的max-widthgithub

移動優先:web

/* iphone6 7 8 */
body {
    background-color: yellow;
}
/* iphone 5 */
@media screen and (max-width: 320px) {
    body {
      background-color: red;
    }
}
/* iphoneX */
@media screen and (min-width: 375px) and (-webkit-device-pixel-ratio: 3) {
    body {
      background-color: #0FF000;
    }
}
/* iphone6 7 8 plus */
@media screen and (min-width: 414px) {
    body {
      background-color: blue;
    }
}
/* ipad */
@media screen and (min-width: 768px) {
    body {
      background-color: green;
    }
}
/* ipad pro */
@media screen and (min-width: 1024px) {
    body {
      background-color: #FF00FF;
    }
}
/* pc */
@media screen and (min-width: 1100px) {
    body {
      background-color: black;
    }
}
複製代碼

PC優先:

/* pc width > 1024px */
    body {
        background-color: yellow;
    }
/* ipad pro */
@media screen and (max-width: 1024px) {
    body {
        background-color: #FF00FF;
    }
}
/* ipad */
@media screen and (max-width: 768px) {
    body {
        background-color: green;
    }
}
/* iphone6 7 8 plus */
@media screen and (max-width: 414px) {
    body {
        background-color: blue;
    }
}
/* iphoneX */
@media screen and (max-width: 375px) and (-webkit-device-pixel-ratio: 3) {
    body {
        background-color: #0FF000;
    }
}
/* iphone6 7 8 */
@media screen and (max-width: 375px) and (-webkit-device-pixel-ratio: 2) {
    body {
        background-color: #0FF000;
    }
}
/* iphone5 */
@media screen and (max-width: 320px) {
    body {
        background-color: #0FF000;
    }
}
複製代碼

2.百分比佈局

經過百分比單位,可使得瀏覽器中組件的寬和高隨着瀏覽器的高度的變化而變化,從而實現響應式的效果。Bootstrap裏面的柵格系統就是利用百分比來定義元素的寬高,CSS3支持最大最小高,能夠將百分比和max(min)一塊兒結合使用來定義元素在不一樣設備下的寬高。

/* pc width > 1100px */
html, body { margin: 0;padding: 0;width: 100%;height: 100%;}
aside {
    width: 10%;
    height: 100%;
    background-color: red;
    float: left;
}
main {
    height: 100%;
    background-color: blue;
    overflow: hidden;
}
/* ipad pro */
@media screen and (max-width: 1024px) {
    aside {
      width: 8%;
      background-color: yellow;
    }
}
/* ipad */
@media screen and (max-width: 768px) {
    aside {
      float: none;
      width: 100%;
      height: 10%;
      background-color: green;
    }
    main {
      height: calc(100vh - 10%);
      background-color: red;
    }
}
/* iphone6 7 8 plus */
@media screen and (max-width: 414px) {
    aside {
      float: none;
      width: 100%;
      height: 5%;
      background-color: yellow;
    }
    main {
      height: calc(100vh - 5%);
      background-color: red;
    }
}
/* iphoneX */
@media screen and (max-width: 375px) and (-webkit-device-pixel-ratio: 3) {
    aside {
      float: none;
      width: 100%;
      height: 10%;
      background-color: blue;
    }
    main {
      height: calc(100vh - 10%);
      background-color: red;
    }
}
/* iphone6 7 8 */
@media screen and (max-width: 375px) and (-webkit-device-pixel-ratio: 2) {
    aside {
      float: none;
      width: 100%;
      height: 3%;
      background-color: black;
    }
    main {
      height: calc(100vh - 3%);
      background-color: red;
    }
}
/* iphone5 */
@media screen and (max-width: 320px) {
    aside {
      float: none;
      width: 100%;
      height: 7%;
      background-color: green;
    }
    main {
      height: calc(100vh - 7%);
      background-color: red;
    }
}
複製代碼

可是咱們必需要弄清楚css中子元素的百分比究竟是相對誰的百分比。直接上結論吧:

子元素的heightwidth中使用百分比,是相對於子元素的直接父元素,width相對於父元素的widthheight相對於父元素的height;子元素的topbottom若是設置百分比,則相對於直接非static定位(默認定位)的父元素的高度,一樣子元素的leftright若是設置百分比,則相對於直接非static定位(默認定位的)父元素的寬度;子元素的padding若是設置百分比,不管是垂直方向或者是水平方向,都相對於直接父親元素的width,而與父元素的height無關。跟padding同樣,margin也是如此,子元素的margin若是設置成百分比,不管是垂直方向仍是水平方向,都相對於直接父元素的widthborder-radius不同,若是設置border-radius爲百分比,則是相對於自身的寬度,除了border-radius外,還有好比translatebackground-size等都是相對於自身的;

從上述對於百分比單位的介紹咱們很容易看出若是所有使用百分比單位來實現響應式的佈局,有明顯的如下兩個缺點:

  • 計算困難,若是咱們要定義一個元素的寬度和高度,按照設計稿,必須換算成百分比單位。
  • 能夠看出,各個屬性中若是使用百分比,相對父元素的屬性並非惟一的。好比widthheight相對於父元素的widthheight,而marginpadding無論垂直仍是水平方向都相對比父元素的寬度、border-radius則是相對於元素自身等等,形成咱們使用百分比單位容易使佈局問題變得複雜。

3.rem佈局

REMCSS3新增的單位,而且移動端的支持度很高,Android2.x+,ios5+都支持。rem單位都是相對於根元素html的font-size來決定大小的,根元素的font-size至關於提供了一個基準,當頁面的size發生變化時,只須要改變font-size的值,那麼以rem爲固定單位的元素的大小也會發生響應的變化。 所以,若是經過rem來實現響應式的佈局,只須要根據視圖容器的大小,動態的改變font-size便可(而em是相對於父元素的)。

rem響應式的佈局思想:

  • 通常不要給元素設置具體的寬度,可是對於一些小圖標能夠設定具體寬度值
  • 高度值能夠設置固定值,設計稿有多大,咱們就嚴格有多大
  • 全部設置的固定值都用rem作單位(首先在HTML總設置一個基準值:pxrem的對應比例,而後在效果圖上獲取px值,佈局的時候轉化爲rem值)
  • js獲取真實屏幕的寬度,讓其除以設計稿的寬度,算出比例,把以前的基準值按照比例進行從新的設定,這樣項目就能夠在移動端自適應了

rem佈局的缺點:

在響應式佈局中,必須經過js來動態控制根元素font-size的大小,也就是說css樣式和js代碼有必定的耦合性,且必須將改變font-size的代碼放在css樣式以前

/*上述代碼中將視圖容器分爲10份,font-size用十分之一的寬度來表示,最後在header標籤中執行這段代碼,就能夠動態定義font-size的大小,從而1rem在不一樣的視覺容器中表示不一樣的大小,用rem固定單位能夠實現不一樣容器內佈局的自適應。*/
function refreshRem() {
    var docEl = doc.documentElement;
    var width = docEl.getBoundingClientRect().width;
    var rem = width / 10;
    docEl.style.fontSize = rem + 'px';
    flexible.rem = win.rem = rem;
}
win.addEventListener('resize', refreshRem);
複製代碼

REM佈局也是目前多屏幕適配的最佳方式。默認狀況下咱們html標籤的font-size爲16px,咱們利用媒體查詢,設置在不一樣設備下的字體大小。

/* pc width > 1100px */
html{ font-size: 100%;}
body {
    background-color: yellow;
    font-size: 1.5rem;
}
/* ipad pro */
@media screen and (max-width: 1024px) {
    body {
      background-color: #FF00FF;
      font-size: 1.4rem;
    }
}
/* ipad */
@media screen and (max-width: 768px) {
    body {
      background-color: green;
      font-size: 1.3rem;
    }
}
/* iphone6 7 8 plus */
@media screen and (max-width: 414px) {
    body {
      background-color: blue;
      font-size: 1.25rem;
    }
}
/* iphoneX */
@media screen and (max-width: 375px) and (-webkit-device-pixel-ratio: 3) {
    body {
      background-color: #0FF000;
      font-size: 1.125rem;
    }
}
/* iphone6 7 8 */
@media screen and (max-width: 375px) and (-webkit-device-pixel-ratio: 2) {
    body {
      background-color: #0FF000;
      font-size: 1rem;
    }
}
/* iphone5 */
@media screen and (max-width: 320px) {
    body {
      background-color: #0FF000;
      font-size: 0.75rem;
    }
}
複製代碼

4.視口單位

css3中引入了一個新的單位vw/vh,與視圖窗口有關,vw表示相對於視圖窗口的寬度,vh表示相對於視圖窗口高度,除了vwvh外,還有vminvmax兩個相關的單位。各個單位具體的含義以下:

單位 含義
vw 相對於視窗的寬度,1vw 等於視口寬度的1%,即視窗寬度是100vw
vh 相對於視窗的高度,1vh 等於視口高度的1%,即視窗高度是100vh
vmin vw和vh中的較小值
vmax vw和vh中的較大值

用視口單位度量,視口寬度爲100vw,高度爲100vh(左側爲豎屏狀況,右側爲橫屏狀況)。例如,在桌面端瀏覽器視口尺寸爲650px,那麼 1vw = 650 * 1% = 6.5px(這是理論推算的出,若是瀏覽器不支持0.5px,那麼實際渲染結果多是7px)。

那麼vw或者vh很相似百分比單位。vw和%的區別爲:

單位 含義
% 大部分相對於祖先元素,也有相對於自身的狀況好比(border-radius、translate等)
vw/vh 相對於視窗的尺寸

從對比中咱們能夠發現,vw單位與百分比相似,單確有區別,前面咱們介紹了百分比單位的換算困難,這裏的vw更像"理想的百分比單位"。任意層級元素,在使用vw單位的狀況下,1vw都等於視圖寬度的百分之一。

使用視口單位來實現響應式有兩種作法:

1.僅使用vw做爲CSS單位
  • 對於設計稿的尺寸轉換爲爲單位,咱們使用Sass函數編譯

    //iPhone 6尺寸做爲設計稿基準
    $vm_base: 375; 
    @function vw($px) {
        @return ($px / 375) * 100vw;
    }
    複製代碼
  • 不管是文本仍是佈局寬度、間距等都使用vw做爲單位

    .mod_nav {
        background-color: #fff;
        &_list {
            display: flex;
            padding: vm(15) vm(10) vm(10); // 內間距
            &_item {
                flex: 1;
                text-align: center;
                font-size: vm(10); // 字體大小
                &_logo {
                    display: block;
                    margin: 0 auto;
                    width: vm(40); // 寬度
                    height: vm(40); // 高度
                    img {
                        display: block;
                        margin: 0 auto;
                        max-width: 100%;
                    }
                }
                &_name {
                    margin-top: vm(2);
                }
            }
        }
    }
    複製代碼
  • 1物理像素線(也就是普通屏幕下1px,高清屏幕下0.5px的狀況)採用transform屬性scale實現

    .mod_grid {
        position: relative;
        &::after {
            // 實現1物理像素的下邊框線
            content: '';
            position: absolute;
            z-index: 1;
            pointer-events: none;
            background-color: #ddd;
            height: 1px;
            left: 0;
            right: 0;
            top: 0;
            @media only screen and (-webkit-min-device-pixel-ratio: 2) {
                -webkit-transform: scaleY(0.5);
                -webkit-transform-origin: 50% 0%;
            }
        }
        ...
    }
    複製代碼
  • 對於須要保持寬高比的圖,應該用padding-top實現

    .mod_banner {
        position: relative;
        padding-top: percentage(100/700); // 使用padding-top
        height: 0;
        overflow: hidden;
        img {
            width: 100%;
            height: auto;
            position: absolute;
            left: 0;
            top: 0; 
        }
    }
    複製代碼
2.搭配vw和rem

雖然採用vw適配後的頁面效果很好,可是它是利用視口單位實現的佈局,依賴視口大小而自動縮放,不管視口過大仍是太小,它也隨着時候過大或者太小,失去了最大最小寬度的限制,此時咱們能夠結合rem來實現佈局

  • 給根元素大小設置隨着視口變化而變化的vw單位,這樣就能夠實現動態改變其大小

  • 限制根元素字體大小的最大最小值,配合body加上最大寬度和最小寬度

    // rem 單位換算:定爲 75px 只是方便運算,750px-75px、640-64px、1080px-108px,如此類推
    $vm_fontsize: 75; // iPhone 6尺寸的根元素大小基準值
    @function rem($px) {
         @return ($px / $vm_fontsize ) * 1rem;
    }
    // 根元素大小使用 vw 單位
    $vm_design: 750;
    html {
        font-size: ($vm_fontsize / ($vm_design / 2)) * 100vw; 
        // 同時,經過Media Queries 限制根元素最大最小值
        @media screen and (max-width: 320px) {
            font-size: 64px;
        }
        @media screen and (min-width: 540px) {
            font-size: 108px;
        }
    }
    // body 也增長最大最小寬度限制,避免默認100%寬度的 block 元素跟隨 body 而過大太小
    body {
        max-width: 540px;
        min-width: 320px;
    }
    複製代碼

5.圖片響應式

這裏的圖片響應式包括兩個方面,一個就是大小自適應,這樣可以保證圖片在不一樣的屏幕分辨率下出現壓縮、拉伸的狀況;一個就是根據不一樣的屏幕分辨率和設備像素比來儘量選擇高分辨率的圖片,也就是當在小屏幕上不須要高清圖或大圖,這樣咱們用小圖代替,就能夠減小網絡帶寬了。

1.使用max-width(圖片自適應):

圖片自適應意思就是圖片能隨着容器的大小進行縮放,能夠採用以下代碼:

img {
    display: inline-block;
    max-width: 100%;
    height: auto;
}
複製代碼

inline-block 元素相對於它周圍的內容之內聯形式呈現,但與內聯不一樣的是,這種狀況下咱們能夠設置寬度和高度。 max-width保證了圖片可以隨着容器的進行等寬擴充(即保證全部圖片最大顯示爲其自身的 100%。此時,若是包含圖片的元素比圖片固有寬度小,圖片會縮放佔滿最大可用空間),而heightauto能夠保證圖片進行等比縮放而不至於失真。若是是背景圖片的話要靈活運用background-size屬性。

那麼爲何不能用width:100%呢?由於這條規則會致使它顯示得跟它的容器同樣寬。在容器比圖片寬得多的狀況下,圖片會被無謂地拉伸。

2.使用srcset
<img srcset="photo_w350.jpg 1x, photo_w640.jpg 2x" src="photo_w350.jpg" alt="">
複製代碼

若是屏幕的dpi = 1的話則加載1倍圖,而dpi = 2則加載2倍圖,手機和mac基本上dpi都達到了2以上,這樣子對於普通屏幕來講不會浪費流量,而對於視網膜屏來講又有高清的體驗。

若是瀏覽器不支持srcset,則默認加載src裏面的圖片。

可是你會發現實際狀況並非如此,在Mac上的Chrome它會同時加載srcset裏面的那張2x的,還會再去加載src裏面的那張,加載兩張圖片。順序是先把全部srcset裏面的加載完了,再去加載src的。這個策略比較奇怪,它竟然會加載兩張圖片,若是不寫src,則不會加載兩張,可是兼容性就沒那麼好。這個多是由於瀏覽器認爲,既然有srcset就不用寫src了,若是寫了src,用戶多是有用的。而使用picture就不會加載兩張

3.使用background-image
.banner{
  background-image: url(/static/large.jpg);
}

@media screen and (max-width: 767px){
  background-image: url(/static/small.jpg);
}
複製代碼
4.使用picture標籤

picturefill.min.js :解決IE等瀏覽器不支持 的問題

<picture>
    <source srcset="banner_w1000.jpg" media="(min-width: 801px)">
    <source srcset="banner_w800.jpg" media="(max-width: 800px)">
    <img src="banner_w800.jpg" alt="">
</picture>

<!-- picturefill.min.js 解決IE等瀏覽器不支持 <picture> 的問題 -->
<script type="text/javascript" src="js/vendor/picturefill.min.js"></script>
複製代碼

picture必需要寫img標籤,不然沒法顯示,對picture的操做最後都是在img上面,例如onload事件是在img標籤觸發的,picturesource是不會進行layout的,它們的寬和高都是0。

另外使用source,還能夠對圖片格式作一些兼容處理:

<picture>
    <source type="image/webp" srcset="banner.webp"> <img src="banner.jpg" alt=""> </picture> 複製代碼

總結:響應式佈局的實現能夠經過媒體查詢+px,媒體查詢+百分比,媒體查詢+rem+js,vm/vh,vm/vh +rem這幾種方式來實現。但每一種方式都是有缺點的,媒體查詢須要選取主流設備寬度尺寸做爲斷點針對性寫額外的樣式進行適配,但這樣作會比較麻煩,只能在選取的幾個主流設備尺寸下呈現完美適配,另外用戶體驗也不友好,佈局在響應斷點範圍內的分辨率下維持不變,而在響應斷點切換的瞬間,佈局帶來斷層式的切換變化,如同卡帶的唱機般「咔咔咔」地一下又一下。經過百分比來適配首先是計算麻煩,第二各個屬性中若是使用百分比,其相對的元素的屬性並非惟一的,這樣就形成咱們使用百分比單位容易使佈局問題變得複雜。經過採用rem單位的動態計算的彈性佈局,則是須要在頭部內嵌一段腳原本進行監聽分辨率的變化來動態改變根元素字體大小,使得CSSJS 耦合了在一塊兒。經過利用純css視口單位實現適配的頁面,是既能解決響應式斷層問題,又能解決腳本依賴的問題的,可是兼容性尚未徹底能結構接受。

響應式佈局的成型方案

如今的css,UI框架等都已經考慮到了適配不一樣屏幕分辨率的問題,實際項目中咱們能夠直接使用這些新特性和框架來實現響應式佈局。能夠有如下選擇方案:

  • 利用上面的方法本身來實現,好比CSS3 Media Query,rem,vw等
  • Flex彈性佈局,兼容性較差
  • Grid網格佈局,兼容性較差
  • Columns柵格系統,每每須要依賴某個UI庫,如Bootstrap

響應式佈局的要點

在實際項目中,咱們可能須要綜合上面的方案,好比用rem來作字體的適配,用srcset來作圖片的響應式,寬度能夠用remflex,柵格系統等來實現響應式,而後可能還須要利用媒體查詢來做爲響應式佈局的基礎,所以綜合上面的實現方案,項目中實現響應式佈局須要注意下面幾點:

  • 設置viewport
  • 媒體查詢
  • 字體的適配(字體單位)
  • 百分比佈局
  • 圖片的適配(圖片的響應式)
  • 結合flex,grid,BFC,柵格系統等已經成型的方案

參考文章:

  1. 響應式佈局的經常使用解決方案對比(媒體查詢、百分比、rem和vw/vh)
  2. 純CSS3使用vw和vh視口單位實現自適應
  3. 你真的瞭解響應式佈局嗎?
  4. 移動端H5頁面 iphone6的適配技巧
  5. 響應式開發心得
  6. 詳解前端響應式佈局、響應式圖片,與自制柵格系統
  7. 基於媒體查詢和 rem 的響應式佈局實踐
  8. 從網易與淘寶的font-size思考前端設計稿與工做流
  9. 移動端前端適配方案對比
相關文章
相關標籤/搜索