詳解前端響應式佈局、響應式圖片,與自制柵格系統

白小明發表於2017-11-24

響應式佈局簡介

什麼是響應式?同一個頁面在不同螢幕尺寸下有不同的佈局。

傳統的開發方式是PC端開發一套,手機端再開發一套,而使用響應式佈局只要開發一套就好了,缺點是CSS比較重。

如下圖所示:

響應式佈局方案選擇

響應式設計可選擇的方案有:

  • CSS3 Media Query(推薦):媒體查詢,相容到IE9+,但可以通過外掛相容IE6-8
  • Flex:彈性佈局,相容性較差(IE10+)
  • Grid:網格佈局,相容性更差
  • Columns:柵格系統,往往需要依賴於某個UI庫,如bootstrap

響應式開發最不好不要雜合使用rem

CSS3 Media Query簡介

利用CSS3 Media Query可以輕鬆實現不同螢幕寬度時切換不同的頁面佈局

相容性:IE9+

目前實現CSS3 Media Query 對於IE相容的庫比較成熟的有respond.jscss3-mediaqueries-js,它們各有優劣

  • respond.js(推薦):壓縮後1k,只實現了media query中最常用的min-width max-width的相容;
  • css3-mediaqueries-js:基本實現了所有css3規範中的media query特性的相容,所以導致壓縮有16k,測試反饋其效能遠低於respond.js;
<!-- HTML5 shiv and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
  <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
  <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->複製程式碼

CSS3 Media Query使用

引入方式分為內嵌樣式和外部樣式,建議使用內嵌樣式

使用內嵌樣式時,建議將響應式程式碼寫在對應正常樣式的下面,不分開書寫,以便維護

// 螢幕尺寸大於 480px 時應用該規則
@media screen and (min-width: 480px) {
  .header {
    background: red;
  }
}

// 螢幕尺寸小於 800px 時應用該規則
@media screen and (max-width: 800px) {}複製程式碼

邏輯操作符:

and or not only複製程式碼

斷點的選擇(頁寬)

自(chao)制(xi)柵格系統

主要抄襲 Bootstrap 和 Layui

為什麼要造輪子?柵格系統往往依賴於某個UI框架,但是往往我們不需要那些繁多的元件和樣式,只需要一個柵格就夠了,鑑於此需求,我自(chao)制(xi)了一套柵格系統

柵格系統用於處理頁面多終端適配的問題。柵格的響應式能力,得益於CSS3媒體查詢(Media Queries)的強力支援,通常針對四類不同尺寸的螢幕進行相應的適配處理

為什麼是12列:因為12的公約數多,是1,2,3,4,6的最小公倍數,相對較靈活。

一、柵格佈局規則:

  1. 採用 .container(在小螢幕以上的裝置中固定寬度) 和 .container-fluid(流體容器,寬度將不會固定,而是 100% 適應)作為佈局容器。

  2. 採用 row 來定義行,如:<div class="row"></div>

    • 行必須包含在佈局容器中,以便為其賦予合適的排列和內補。
    • 只有列(col)可以作為行(row)的直接子元素
  3. 採用類似 col-md-* 來定義一組列,且放在行內。

    • 變數 * 代表的是該列所佔用的12等分數值,可選值為 1 - 12。
    • 如果多個列的等分數值總和等於12,則剛好滿行排列。如果大於12,多餘的列將自動另起一行。
  4. 可對列追加類似 col-space-*col-md-offset-* 這樣的預設類來定義列間距和列偏移。

    • 如果列間距大於30px,請採用列偏移

二、響應式規則:

三、響應式佈局心得:

  1. 對於排版差異大的區域,寫兩份程式碼,大屏的時候隱藏掉小屏的html,小屏的時候隱藏掉大屏的html標籤。並且這種情況不應該是常例,如果你經常要寫兩套,那說明你這個頁面可能不太適合寫響應式,還不如直接寫兩套呢。

  2. 左右佈局變成上下佈局,把右邊的內容往下面放。

  3. 字號、間距變小

/**
 * 作品:myreset.css
 * 維護:白小明
 * 更新:2017年11月24日
 * 理念:1. 適應中文,基於最新主流瀏覽器
 *       2. reset 的目的不是清除瀏覽器的預設樣式,這僅是部分工作。清除和重置是緊密不可分的。
 *       3. reset 的目的不是讓預設樣式在所有瀏覽器下一致,而是減少預設樣式有可能帶來的問題。
 */

/* reset
   ========================================================================== */
@charset "utf-8";
body, h1, h2, h3, h4, h5, h6, hr, p, blockquote,
pre,dl, dt, dd, ul, ol, li,
form, fieldset, lengend, button, input, textarea,
th, td { margin: 0; padding: 0; }

ul, ol { list-style: none; }
a { text-decoration: none; }
q:before, q:after { content: ''; }
legend { color: #000; }
table { border-collapse: collapse; border-spacing: 0; }
button, textarea { font-size: 100%; border: 0; }
fieldset, img { border: 0; }

a:hover { -webkit-transition: all .5s; transition: all .5s; }

*, *:after, *:before { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }

/* common
   ========================================================================== */
.fl { float: left; *display: inline; _display:inline; }
.fr { float: right; *display: inline; _display:inline; }
.clearfix:after { display: block; clear: both; content: ''; visibility: hidden; height: 0; }
.clearfix { *zoom: 1; }

/* 柵格系統,移動裝置優先
   ========================================================================== */
.container { margin-left: auto; margin-right: auto; padding-left: 15px; padding-right: 15px; }
.container-fluid { margin-left: auto; margin-right: auto; padding-left: 15px; padding-right: 15px; }

.row { margin-left: -15px; margin-right: -15px; }
.row:before, .row:after { content: ""; display: table; clear: both; }

.hide-xs { display: none!important; }
.show-xs-block { display: block!important; }
.show-xs-inline { display: inline!important; }
.show-xs-inline-block { display: inline-block!important; }

.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1,
.col-xs-2, .col-sm-2, .col-md-2, .col-lg-2,
.col-xs-3, .col-sm-3, .col-md-3, .col-lg-3,
.col-xs-4, .col-sm-4, .col-md-4, .col-lg-4,
.col-xs-5, .col-sm-5, .col-md-5, .col-lg-5,
.col-xs-6, .col-sm-6, .col-md-6, .col-lg-6,
.col-xs-7, .col-sm-7, .col-md-7, .col-lg-7,
.col-xs-8, .col-sm-8, .col-md-8, .col-lg-8,
.col-xs-9, .col-sm-9, .col-md-9, .col-lg-9,
.col-xs-10, .col-sm-10, .col-md-10, .col-lg-10,
.col-xs-11, .col-sm-11, .col-md-11, .col-lg-11,
.col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
  position: relative;
  min-height: 1px;
  padding-right: 15px;
  padding-left: 15px;
}

.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6,
.col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
  float: left;
}

.col-xs-1 { width: 8.33333333%; }
.col-xs-2 { width: 16.66666667%; }
.col-xs-3 { width: 25%; }
.col-xs-4 { width: 33.33333333%; }
.col-xs-5 { width: 41.66666667%; }
.col-xs-6 { width: 50%; }
.col-xs-7 { width: 58.33333333%; }
.col-xs-8 { width: 66.66666667%; }
.col-xs-9 { width: 75%; }
.col-xs-10 { width: 83.33333333%; }
.col-xs-11 { width: 91.66666667%; }
.col-xs-12 { width: 100%; }

.col-xs-offset-1 { margin-left: 8.33333333%; }
.col-xs-offset-2 { margin-left: 16.66666667%; }
.col-xs-offset-3 { margin-left: 25%; }
.col-xs-offset-4 { margin-left: 33.33333333%; }
.col-xs-offset-5 { margin-left: 41.66666667%; }
.col-xs-offset-6 { margin-left: 50%; }
.col-xs-offset-7 { margin-left: 58.33333333%; }
.col-xs-offset-8 { margin-left: 66.66666667%; }
.col-xs-offset-9 { margin-left: 75%; }
.col-xs-offset-10 { margin-left: 83.33333333%; }
.col-xs-offset-11 { margin-left: 91.66666667%; }
.col-xs-offset-12 { margin-left: 100%; }

@media screen and (min-width: 768px) {
  .container { width: 750px; }

  .hide-sm { display: none!important; }
  .show-sm-block { display: block!important; }
  .show-sm-inline { display: inline!important; }
  .show-sm-inline-block { display: inline-block!important; }

  .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6,
  .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {
    float: left;
  }

  .col-sm-1 { width: 8.33333333%; }
  .col-sm-2 { width: 16.66666667%; }
  .col-sm-3 { width: 25%; }
  .col-sm-4 { width: 33.33333333%; }
  .col-sm-5 { width: 41.66666667%; }
  .col-sm-6 { width: 50%; }
  .col-sm-7 { width: 58.33333333%; }
  .col-sm-8 { width: 66.66666667%; }
  .col-sm-9 { width: 75%; }
  .col-sm-10 { width: 83.33333333%; }
  .col-sm-11 { width: 91.66666667%; }
  .col-sm-12 { width: 100%; }

  .col-sm-offset-1 { margin-left: 8.33333333%; }
  .col-sm-offset-2 { margin-left: 16.66666667%; }
  .col-sm-offset-3 { margin-left: 25%; }
  .col-sm-offset-4 { margin-left: 33.33333333%; }
  .col-sm-offset-5 { margin-left: 41.66666667%; }
  .col-sm-offset-6 { margin-left: 50%; }
  .col-sm-offset-7 { margin-left: 58.33333333%; }
  .col-sm-offset-8 { margin-left: 66.66666667%; }
  .col-sm-offset-9 { margin-left: 75%; }
  .col-sm-offset-10 { margin-left: 83.33333333%; }
  .col-sm-offset-11 { margin-left: 91.66666667%; }
  .col-sm-offset-12 { margin-left: 100%; }
}

@media screen and (min-width: 992px) {
  .container { width: 970px; }

  .hide-md { display: none!important; }
  .show-md-block { display: block!important; }
  .show-md-inline { display: inline!important; }
  .show-md-inline-block { display: inline-block!important; }

  .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6,
  .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {
    float: left;
  }

  .col-md-1 { width: 8.33333333%; }
  .col-md-2 { width: 16.66666667%; }
  .col-md-3 { width: 25%; }
  .col-md-4 { width: 33.33333333%; }
  .col-md-5 { width: 41.66666667%; }
  .col-md-6 { width: 50%; }
  .col-md-7 { width: 58.33333333%; }
  .col-md-8 { width: 66.66666667%; }
  .col-md-9 { width: 75%; }
  .col-md-10 { width: 83.33333333%; }
  .col-md-11 { width: 91.66666667%; }
  .col-md-12 { width: 100%; }

  .col-md-offset-1 { margin-left: 8.33333333%; }
  .col-md-offset-2 { margin-left: 16.66666667%; }
  .col-md-offset-3 { margin-left: 25%; }
  .col-md-offset-4 { margin-left: 33.33333333%; }
  .col-md-offset-5 { margin-left: 41.66666667%; }
  .col-md-offset-6 { margin-left: 50%; }
  .col-md-offset-7 { margin-left: 58.33333333%; }
  .col-md-offset-8 { margin-left: 66.66666667%; }
  .col-md-offset-9 { margin-left: 75%; }
  .col-md-offset-10 { margin-left: 83.33333333%; }
  .col-md-offset-11 { margin-left: 91.66666667%; }
  .col-md-offset-12 { margin-left: 100%; }
}

@media screen and (min-width: 1200px) {
  .container { width: 1170px; }

  .hide-lg { display: none!important; }
  .show-lg-block { display: block!important; }
  .show-lg-inline { display: inline!important; }
  .show-lg-inline-block { display: inline-block!important; }

  .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6,
  .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {
    float: left;
  }

  .col-lg-1 { width: 8.33333333%; }
  .col-lg-2 { width: 16.66666667%; }
  .col-lg-3 { width: 25%; }
  .col-lg-4 { width: 33.33333333%; }
  .col-lg-5 { width: 41.66666667%; }
  .col-lg-6 { width: 50%; }
  .col-lg-7 { width: 58.33333333%; }
  .col-lg-8 { width: 66.66666667%; }
  .col-lg-9 { width: 75%; }
  .col-lg-10 { width: 83.33333333%; }
  .col-lg-11 { width: 91.66666667%; }
  .col-lg-12 { width: 100%; }

  .col-lg-offset-1 { margin-left: 8.33333333%; }
  .col-lg-offset-2 { margin-left: 16.66666667%; }
  .col-lg-offset-3 { margin-left: 25%; }
  .col-lg-offset-4 { margin-left: 33.33333333%; }
  .col-lg-offset-5 { margin-left: 41.66666667%; }
  .col-lg-offset-6 { margin-left: 50%; }
  .col-lg-offset-7 { margin-left: 58.33333333%; }
  .col-lg-offset-8 { margin-left: 66.66666667%; }
  .col-lg-offset-9 { margin-left: 75%; }
  .col-lg-offset-10 { margin-left: 83.33333333%; }
  .col-lg-offset-11 { margin-left: 91.66666667%; }
  .col-lg-offset-12 { margin-left: 100%; }
}複製程式碼

響應式圖片

最後再聊聊響應式圖片

一般地,相同的banner,在電腦上需要使用大圖,但是手機上面使用小圖就好了,不然會造成手機上載入慢浪費流量等問題,響應式圖片應運而生。如何實現呢?

1. css3 media query

一個辦法是使用backgound-image結合媒體查詢,如下所示:

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

@media screen and (max-width: 767px){
  background-image: url(/static/small.jpg);
}複製程式碼

這種方法的缺點是對SEO不太友好,因為如果使用img標籤還可以寫個alt屬性。

2. 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>複製程式碼

如上,如果頁面寬度大於800px(PC),則載入大圖,而在手機上載入小圖。這樣寫瀏覽器就只會載入source裡面的一張圖片。但是如果是用js動態插進去的,它還是會去載入兩張,只有寫在html裡面載入初始化頁面的時候才只載入一張。

picture必須要寫img標籤,否則無法顯示,對picture的操作最後都是在img上面,例如onload事件是在img標籤觸發的,picture和source是不會進行layout的,它們的寬和高都是0。

另外使用source,還可以對圖片格式做一些相容處理:

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

webp在保持同等清晰度的情況下,體積可以減少一半,但是目前只有Chrome支援,Safari和firefox一直處於實驗階段,所以其它的瀏覽器如firefox將會載入jpg格式的照片:

3. srcset

<img srcset="photo_w350.jpg 1x, photo_w640.jpg 2x" src="photo_w350.jpg" alt="">複製程式碼

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

如果瀏覽器不支援srcset,則預設載入src裡面的圖片。

但是你會發現實際情況並不是如此,在Mac上的Chrome它會同時載入srcset裡面的那張2x的,還會再去載入src裡面的那張,載入兩張圖片。順序是先把所有srcset裡面的載入完了,再去載入src的。這個策略比較奇怪,它居然會載入兩張圖片,如果不寫src,則不會載入兩張,但是相容性就沒那麼好。這個可能是因為瀏覽器認為,既然有srcset就不用寫src了,如果寫了src,使用者可能是有用的。而使用picture就不會載入兩張

製作響應式圖片還有其他的方法,因為沒用過,我就不分析啦。

以上。

相關文章