什麼是響應式?響應式的頁面在不同的螢幕有不同的佈局,換句話説,使用相同的 html 在不同的解析度有不同的排版。如下圖所示:

  響應式佈局是為了解決適配的問題,傳統的開發方式是 PC 端開發一套,手機端再開發一套,而使用響應式佈局只要開發一套就好了。因為它是用的同樣 html,所以它的 JS 邏輯互動也只需寫一套就好了,缺點是 CSS 比較重。
  傳統的手機端適配常見有三種解決方案,種是 bootstrap 的 columns 佈局;第二種是使用全域性的 rem,先根據螢幕換算 1rem 等於多少個 px,然後設定 html 標籤的 font-size 為多少個 rem,螢幕越大,則 font-size 越大,然後頁面所有的元素的寬高和字型大小都用 rem 等比例縮放;第三種是阿里的 flex box,這種方案和第二種類似,不同點是頁面內容的字型大小是用的 px,而不是比例縮放的 rem 。種需要額外引入一個框架。第三種相對第二種來説應該更合理點,因為正文的字型常用的為 14px 或者 16px,如果一個頁面在這個手機字號是 15.5px,在另外一個手機又變成了 14.9px,這樣可能會有點奇怪。
  而使用響應式佈局就不需要進行 rem 的換算,下面通過上圖的那個例子一步一步地分析怎麼做響應式。
  1. 設定不同解析度頁面兩邊留白

  先一個頁面的主體內容有大的寬度,當螢幕超過這個寬度時這個中間的主體內容大就這麼大了,不會再變大了,也就是説它固定一個大寬度,然後居中顯示,如大為 1080px 。然後當大於 1024px 時,頁面主體內容小寬為 960px,兩邊自動留白;在 500px 到 1024px 之間兩邊保持留白 40px;而當小於 500px 時就認為是手機,兩邊留白 20px 。所以計算一下,container 的代碼如下:

  總體的思想是留白要合適,既不能留太多,導致中間內容太窄,也不能讓中間的內容顯得太大。這個其實和 bootstrap 的 container 思想一致,只是您可能要根據您自己的業務特點、多用户人羣等做不同留白策略。
  2. 螢幕變小時,一頭變窄,另一頭不變

  當螢幕變小或者瀏覽器視窗拉小時,中間內容的寬度就不能保持 1080px,它得跟着變小,而在變小的過程中,往往要保持一邊不變,另一邊隨頁面變窄,如下圖所示:

  右邊的結果欄寬度保持不變,左邊的表單欄寬度縮小。因為右邊一旦就窄不好看了,如果右邊變窄,那麼字型也要相應縮小,字號一縮小,右邊上下留白就變得太大,這樣就不美觀了,所以只能採取右邊保持不動的策略去縮小左邊的內容。這種場景比較常見,右邊如果是一個頭像的話,它也不能跟着縮小,它一縮小高度也要跟着縮小,導致上下太空,所以這種情況也不能動。
  3. 保持中間留白固定,縮小內容寬度

  左欄的寬度變小應該怎麼變呢?有一個原則,就是要保持中間的間距固定,而兩邊的內容寬度相應縮小,如下圖所示:

  所以就要藉助 CSS3 的 calc,如下所示:
  1
  2
  3
  input{
  width:calc((100%-20px)/2)
  }
  calc 的相容性 IE10 及以上支持,android 4 及以下不支持,所以考慮到不支持的裝置,可以簡單做個相容,如下代碼所示:
  1
  2
  3
  4
  input{
  width:48%;
  width:calc((100%-20px)/2);
  }
  如果不支持 calc 就用 48%,這樣差別其實不是很大,就是不是很精確。真的需要的話,您可以多寫幾個媒體查詢變得更精確。
  4. 左右佈局變成上下佈局
  當螢幕拉得很小的時候,左欄已經縮得很小了,再變小就不協調了,所以這個時候要把左右佈局改成上下佈局,把右邊的內容往下面放。因為右欄在大屏的時候是 float:right,所以在中屏的時候覆蓋掉這個浮動的屬性,變成 float:none 就可以了。原本右欄的內容有四行,都比較短,可以考慮把它下面的三行排成一行,即讓它們浮動。如下面代碼所示:
  1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  11
  12
  13
  14
  15
  .cal-result{
  float:right;
  width:330px;
  }
  1
   media(max-width:800px){
  .cal-result{
  float:none;
  width:100%;
  }
  .cal-result.result{
  float:left;
  width:33%;
  }
  }

  讓每一個 result 佔 1/3,然後浮動,效果如下:

  5. 寬度太小時,自動換行
  特別是當內容是列表 ul 形式的時候,排不下的 li 應當自動換到下一行。當然也可以手動控制,如下:
  1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  11
   media(max-width:800px){
  .result{
  width:33%;
  }
  }
  1
   media(max-width:400px){
  .result{
  width:50%;
  }
  }
  在螢幕寬度小於 400 的時候,每個結果就佔 50%,這樣就排成兩行了。這也是一種常用的辦法,但是在我們這個例子,如果數字比較小,在 iPhone6 375px 的螢幕上還是排得下的,如果能保持在一行相對比較美觀。而且固定 50%,如果當數字比較大時也有可能會有重疊的危險,這個也有辦法,就是別寫死寬度,而是寫死 min-width 為 50%,這樣當內容比較長時,float 的元素同一行排不下就會自動換行。但是知名還是要個辦法讓它能根據內容長度自動換行,當然可以用 JS 計算,但是有點麻煩。
  這個時候 flex 就派上用場了,很簡單,只要設定兩個屬性:
  1
  2
  3
  4
  5
  .result-container{
  display:flex;
  justify-content:space-between;
  flex-wrap:wrap;
  }

  space-between 讓子元素挨着容器的兩邊等間距排列,而 wrap 屬性讓子元素自動換行,當容器寬度不夠的時候,就有了以下的效果:

  這樣還有一個小問題,就是當內容如果剛剛好佔滿時,兩個項之間就沒有間距了,如下圖所示:

  這樣就貼在一起了,由於 flex 的 space-between 不能指定小的 space,所以只通過 margin 或者 padding 的方法,如給元素新增 margin-right:
  1
  2
  3
  .result:not(:last-child){
  margin-right:10px;
  }

  效果如下:

  這樣比貼在一起顯示的效果好。
  還有從大屏變成成小屏的時候有些字號主要是標題的字號和間距要相應調小,這種變小是階梯變化的,而不是像 rem 一樣連續變化,而且這種階梯一般只要有兩個就夠了,一個大屏的,一個小屏的。如果您需要做很多階梯的話,那您的排版很可能有問題。
  6. 使用響應式圖片
  如相同的頭圖,在電腦上需要使用大圖,但是手機上面使用小圖就好了,不然會造成手機上載入慢浪費流量等問題,一個辦法是使用 backgound-image 結合媒體查詢,如下所示:
  1
  2
  3
  4
  5
  6
  7
  .banner{
  background-image:url(/static/large.jpg);
  }
   media(max-width:500px){
  background-image:url(/static/small.jpg);
  }
  這種方法的缺點是對 SEO 站羣不太友好,因為如果使用 img 標籤還可以寫個 alt 屬性。
  第二種常用辦法是使用 img 的 srcset 或者 picture 標籤做響應式圖片,這個我在《Effective 前端 7:加快頁面開啓速度》已經提到,這裏不再重複。
  這種響應式圖片除了大小屏之外,還可以兼顧視網屏即 dpr 為 2 及以上的和普通屏 dpr 為 1 的螢幕,即在高 dpr 的螢幕使用 2 倍圖,而普通螢幕使用 1 倍圖。
  7. 其它問題處理
  有些地方大小屏的排版差異比較大,例如有些內容大屏的時候是挨在一起,而小屏離得比較遠,這個時候您可能得重複 html,寫兩份的標籤,大屏的時候隱藏掉小屏的 html 標籤,小屏的時候隱藏掉大屏的 html 標籤。並且這種情況不應該是常例,如果您經常要寫兩套,那説明您這個頁面可能不太適合寫響應式,還不如直接寫兩套呢。
  還有個問題,有時候您可能要藉助 rem/transform:scale 做大小縮放,但這一定是下策,我們的原則還是要保持字號和間距不變,當螢幕的跨度不是很大的時候。使用 transform 的後果是螢幕拉小的時候,內容跟着變小了,但是由於 transform 不會造成重排,它佔據的高度還是那麼大,下面的內容不會跟上來。這樣就得手動計算內容的高度。另外如果使用 rem,就和響應式的思想衝突了。如果頁面的一部分字號使用了 rem,另一部分字號使用了 px,這樣就不協調了,如果您全部寫 rem 那就不需要使用響應式開發了。這個時候您可能要想一想,是不是 UI 出得有問題。讓 UI 重新調整。
  還有,有時候可能會用到高度的媒體查詢,例如在高度小於多少的時候,不能讓彈框超出頁面的高度;在高度大於多少的時候,讓 footer 的定位 fixed 在底部,不然 footer 的下面可能會留白。