Flex佈局徹底教程

原文:A Complete Guide to Flexbox
做者:CHRIS COYIER
譯者:Shelley Lee
本文同時發佈於知乎專欄:前端指南
轉載需提早聯繫譯者,未經容許不得轉載。css

背景介紹

Flexbox 佈局(也叫Flex佈局,彈性盒子佈局)模塊目標在於提供一個更有效地佈局、對齊方式,而且可以使父元素在子元素的大小未知或動態變化狀況下仍然可以分配好子元素之間的間隙。html

Flex佈局的主要思想是使父元素可以調節子元素的高度、寬度和排布的順序,從而可以最好地適應可用佈局空間(可以適應不一樣的設備和不一樣大小的屏幕)。設定爲flex佈局的父元素(容器)可以放大子元素使之儘量填充可用空間,也能夠收縮子元素使之不溢出。前端

最重要的是,與傳統佈局中塊狀元素按照垂直方向擺放,行內元素按照水平方向擺放相比,flex佈局是無方向的。傳統佈局在應對大型複雜的佈局時缺少靈活性,特別是在改變方向、改變大小、伸展、收縮等等方面。css3

: Flex 佈局比較適合小規模的佈局,Gird佈局面向更大規模的佈局。git

基本概念

Flex佈局是一個完整的模塊而不是一個單獨的屬性,它包括了完整的一套屬性。其中有的屬性是設置在容器(container,也能夠叫作父元素,稱爲flex container)上,有的則是設置在容器的項目上(item,也能夠叫作子元素,稱爲flex items)上。github


譯者注:因爲item譯成項目不夠直觀和形象,如下統一用父元素指代container,子元素指代item。web

若是咱們能夠說傳統佈局是創建在塊狀元素垂直流和行內元素水平流上的,那麼flex佈局就是創建在"flex-flow方向"上的,經過下圖解釋flex佈局的主要思想。shell

在flex佈局中,子元素要麼按照主軸也就是main axis(從main-startmain-end)排布,要麼按照交叉軸,也就是cross axis(從cross-startcross-end)排布。瀏覽器

下面介紹幾個概念:app

  • __main axis__: Flex 父元素的主軸是指子元素佈局的主要方向軸,注意主軸不必定是水平的,它由屬性flex-direction來肯定主軸是水平仍是垂直的(後面會介紹)。

  • __main-start|main-end__: 分別表示主軸的開始和結束,子元素在父元素中會沿着主軸從main-startmain-end排布。

  • __main size__: 單個項目佔據主軸的長度大小。

  • __cross axis__: 交叉軸,與主軸垂直。

  • __cross-start|cross-end__: 分別表示交叉軸的開始和結束。子元素在交叉軸的排布從cross-start開始到cross-end

  • __cross size__: 子元素在交叉軸方向上的大小。

屬性介紹

屬性分做用於父元素的屬性和做用於子元素的屬性兩部分介紹。

父元素屬性

display

用來定義父元素是一個 flex佈局容器。若是設置爲flex則父元素爲塊狀元素,設置爲inline-flex父元素呈現爲行內元素。

.container {
  display: flex; /* or inline-flex */
}

flex-direction

flex-direction定義flex佈局的主軸方向。flex佈局是單方向佈局,子元素主要沿着水平行或者垂直列布局。

.container {
  flex-direction: row | row-reverse | column | column-reverse;
}
  • row: 行方向,flex-direction的默認值,在ltr(left to right, 從左到右)排版方式下從左到右排列,在rtl(right to left, 從右到左)排版方式下從右到左排列。

  • row-reverse: 行反方向,在ltr中從右向左,在rtl中從左到右。

  • column: 列方向,與row類似,只是從上到下。

  • column-reverse: 列反方向,與row-reverse類似,只是從下島上。

flex-wrap

默認狀況下,flex佈局中父元素會把子元素儘量地排在同一行,經過設置flex-wrap來決定是否容許子元素這行排列。

.container{
  flex-wrap: nowrap | wrap | wrap-reverse;
}
  • nowrap: 不折行,默認值,全部的子元素會排在一行。

  • wrap: 折行,子元素會從上到下根據需求折成多行。

  • wrap-reverse: 從下向上折行,子元素會從下島上根據需求折成多行。

這裏有一些可視化的示例

flex-flow

flex-flowflex-directionflex-wrap屬性的縮寫形式。默認值是row,nowrap

flex-flow: <‘flex-direction’> || <‘flex-wrap’>

justify-content

justify-content屬性定義了子元素沿主軸方向的對齊方式,用來當子元素大小最大的時候,分配主軸上的剩餘空間。也能夠當子元素超出主軸的時候用來控制子元素的對齊方式。

  • flex-start: 默認值,朝主軸開始的方向對齊。

  • flex-end: 朝主軸結束的方向對齊。

  • center: 沿主軸方向居中。

  • space-between: 沿主軸兩端對齊,第一個子元素在主軸起點,最後一個子元素在主軸終點。

  • space-around: 沿主軸子元素之間均勻分佈。要注意的是子元素看起來間隙是不均勻的,第一個子元素和最後一個子元素離父元素的邊緣有一個單位的間隙,但兩個子元素之間有兩個單位的間隙,由於每一個子元素的兩側都有一個單位的間隙。

.container {
  justify-content: flex-start | flex-end | center | space-between | space-around;
}

align-items

align-items定義了子元素在交叉軸方向的對齊方向,這是在每一個子元素仍然在其原來所在行的基礎上所說的。能夠看做是交叉軸上的justify-content屬性;

.container {
  align-items: flex-start | flex-end | center | baseline | stretch;
}
  • flex-start: 按照交叉軸的起點對齊。

  • flex-end: 按照交叉軸的終點對齊。

  • center: 沿交叉軸方向居中。

  • baseline: 按照項目的第一行文字的基線對齊。

  • stretch: 默認值,在知足子項目所設置的min-heightmax-heightheight的狀況下拉伸子元素使之填充整個父元素。

align-content

align-content是當父元素所包含的行在交叉軸方向有空餘部分時如何分配空間。與justify-content在主軸上如何對單個子元素對齊很類似。

注意:當只有一行的時候,該屬性並不起做用。

.container {
  align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}

譯者注:該屬性中的六個屬性值與justify-content中的六個屬性意思類似,不一樣之處在於justify-content沿主軸方向的做用於單個子元素,而align-content沿交叉軸方向做用於行。遂再也不贅述各屬性值含義。

譯者注:注意align-itemsalign-content的區別,前者是指在單行內的子元素對齊方式,後者是指多行之間的對齊方式。

父元素屬性總結

display: flex|inline-flex;
  flex-direction: row | row-reverse | column | column-reverse;
  flex-wrap: nowrap | wrap | wrap-reverse;
  flex-flow: <‘flex-direction’> || <‘flex-wrap’>;
  justify-content: flex-start | flex-end | center | space-between | space-around;
  align-items: flex-start | flex-end | center | baseline | stretch;
  align-content: flex-start | flex-end | center | space-between | space-around | stretch;

子元素屬性

order

默認狀況下,子元素按照代碼書寫的前後順序佈局,但order屬性能夠更改子元素出現的順序。

.item {
  order: <integer>;
}

譯者注order的默認值爲0;子元素的order值越小,佈局越排在前面,參考例圖理解。

flex-grow

flex-grow規定在空間容許的狀況下,子元素如何按照比例分配可用剩餘空間。若是全部的子元素的屬性都設定爲1,則父元素中的剩餘空間會等分給全部子元素。若是其中某個子元素的flex-grow設定爲2,則在分配剩餘空間時該子元素將得到其餘元素二倍的空間(至少會盡力得到)。

.item {
  flex-grow: <number>; /* default 0 */
}

flex-grow不接受負值。

譯者注:默認值爲0,意味着即便有剩餘空間,各子元素也不會放大。

flex-shrink

flex-grow屬性相似,flex-shrink定義了空間不足時項目的縮小比例。

.item {
  flex-shrink: <number>; /* default 1 */
}

flex-shrink不接受負值。

譯者注flex-shrink默認值爲1, 當全部子元素都爲默認值時,則空間不足時子元素會同比例縮小。若是其中某個子元素的flex-shrink值爲0,則空間不足時該子元素並不會縮小。若是其中某個子元素的flex-shrink值爲2時,則空間不足時該子元素會以二倍速度縮小。

flex-basis

flex-basis定義了在計算剩餘空間以前子元素默認的大小。能夠設置爲某個長度(e.g. 20%, 5rem, etc.)或者關鍵字。關鍵字auto意味着子元素會按照其原本的大小顯示。關鍵字content意味着根據內容來肯定大小——這個關鍵字到目前沒有被很好地支持,因此測試起來比較困難,與content的相似的關鍵字還有max-content, min-content, fit-content

.item {
  flex-basis: <length> | auto; /* default auto */
}

若是設置爲0, 則子元素內容周圍的空隙不會根據flex-grow按比例分配,若是設置爲auto,則子元素周圍額外的空襲會根據flex-grow按照比例分配,以下圖:

flex

flexflex-growflex-shrinkflex-basis三個屬性的縮寫。其中第二個和第三個參數(flex-grow,flex-basis)是可選的。默認值爲0 1 auto

.item {
  flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
}

推薦使用縮寫形式而不是單獨地設置每個屬性,縮寫形式中會智能地計算出相關值。

align-self

經過設置某個子元素的align-self屬性,能夠覆蓋align-items所設置的對齊方式。屬性值與align-items中的意義相同,再也不贅述。

.item {
  align-self: auto | flex-start | flex-end | center | baseline | stretch;
}

float,clearvertical-align對flex子元素沒有任何影響。

示例

示例一:水平垂直居中

咱們從一個很是很是簡單的例子開始,解決一個咱們常常會遇到的問題:水平垂直居中。若是使用flex佈局會很是簡單。

.parent {
  display: flex;
  height: 300px; /* 隨意設定大小 */
}

.child {
  width: 100px;  /* 隨意設定大小,比父元素要小 */
  height: 100px; /* 同上 */
  margin: auto;  /* 見證奇蹟的時刻 */
}

這個主要緣由是,在flex佈局的父元素中設置marginauto會自動吸取額外的空間,因此設置水平垂直的margin都爲auto會使子元素在水平垂直方向上都完美居中。

示例二:響應式初體驗

如今咱們考慮用更多的屬性。考慮有6個子元素,有固定的大小,可是咱們但願他們可以在改變瀏覽器寬度的時候仍然能夠在水平軸上完美地顯示(注意在不使用媒體查詢的前提下)。

.flex-container {
  /* 首先咱們先建立一個flex佈局上下文 */
  display: flex;
  
  /* 而後咱們定義flex方向和是否容許子元素換行
   * 注意這與如下代碼等價:
   * flex-direction: row;
   * flex-wrap: wrap;
   */
  flex-flow: row wrap;
  
  /* 而後咱們定義在剩餘空間上子元素如何排布 */
  justify-content: space-around;
}

完成。剩下的就是一些其餘樣式如顏色的設置了。

在線示例

改變瀏覽器大小,看看佈局會有什麼變化吧!

示例三:響應式導航欄

讓咱們再嘗試一些別的東西。假設咱們有一個向右對齊的導航欄在咱們網頁的最上端,可是咱們但願它在中屏上顯示時爲居中,在小屏上顯示爲單列。一樣使用flex佈局,實現起來會很簡單。

/* 大屏 */
.navigation {
  display: flex;
  flex-flow: row wrap;
  /* 這裏設置對齊主軸方向的末端 */
  justify-content: flex-end;
}

/* 中屏 */
@media all and (max-width: 800px) {
  .navigation {
    /* 當在中屏上,設置居中,並設置剩餘空間環繞在子元素左右 */
    justify-content: space-around;
  }
}

/* 小屏 */
@media all and (max-width: 500px) {
  .navigation {
    /* 在小屏上,咱們不在使用行做爲主軸,而以列爲主軸 */
    flex-direction: column;
  }
}

在線示例

改變瀏覽器大小,看看佈局會有什麼變化吧!

示例四:移動優先的三欄佈局

咱們經過靈活使用flex佈局嘗試一些更好玩的佈局。來作一個移動優先的3列布局並帶有全屏寬的header和footer。

.wrapper {
  display: flex;
  flex-flow: row wrap;
}

/* 咱們要告訴全部的子元素寬度 100% */
.header, .main, .nav, .aside, .footer {
  flex: 1 100%;
}

/* 移動優先依賴於源代碼默認的渲染順序
 * in this case:
 * 1. header
 * 2. nav
 * 3. main
 * 4. aside
 * 5. footer
 */

/* 中屏 */
@media all and (min-width: 600px) {
  /* 咱們要告訴兩邊的sidebar共享一個行 */
  .aside { flex: 1 auto; }
}

/* 大屏幕 */
@media all and (min-width: 800px) {
  /* 經過order設定各個面板的渲染順序
   * 告訴主要面板元素佔用側欄兩倍的空間
   */
  .main { flex: 2 0px; }
  
  .aside-1 { order: 1; }
  .main    { order: 2; }
  .aside-2 { order: 3; }
  .footer  { order: 4; }
}

在線示例

改變瀏覽器大小,看看佈局會有什麼變化吧!

瀏覽器前綴

Flex佈局須要一些瀏覽器前綴來最大力度地兼容大多數的瀏覽器。Flex佈局的前綴不僅是在屬性前面添加瀏覽器前綴,不一樣瀏覽器下的屬性名和屬性值都不一樣,這是由於Flexbox佈局的標準一直在變,一共有old, tweener, new 三個版本。

可能處理前綴的最好方法是使用新的語法書寫CSS並經過Autoprefixer運行CSS,可以很好地處理這個問題。

另外,這裏有一個Sass中 @mixin 來處理一些前綴,也能夠給你一些處理前綴的啓發:

@mixin flexbox() {
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;
}

@mixin flex($values) {
  -webkit-box-flex: $values;
  -moz-box-flex:  $values;
  -webkit-flex:  $values;
  -ms-flex:  $values;
  flex:  $values;
}

@mixin order($val) {
  -webkit-box-ordinal-group: $val;  
  -moz-box-ordinal-group: $val;     
  -ms-flex-order: $val;     
  -webkit-order: $val;  
  order: $val;
}

.wrapper {
  @include flexbox();
}

.item {
  @include flex(1 200px);
  @include order(2);
}

其餘資源

Bugs

我見過的最棒的flexbox bug總結是Philip Walton 和 Greg Whitworth的Flexbugs,是開源的,你能夠在上面跟蹤動態。

瀏覽器支持

首先看一下Flex佈局的三個版本

  • (new)是指標準中最近的語法(e.g. display:flex;)。

  • (tweener)是指2011年之後非官方的臨時版本(e.g. display:flexbox;)。

  • (old)是指2009年之後的舊語法(e.g. display:box;)

Blackberry browser 10+ 支持新語法。

更多混合使用語法達到最佳瀏覽器兼容,能夠參考this article (CSS-Tricks)或者this article (DevOpera)

譯者的話

網上有很多flex相關教程,但當我看到CHRIS COYIER的這篇文章時,不由被其詳盡所震撼,最近也在撰寫佈局相關的文章,故產生了翻譯此文的想法。翻譯過程當中儘可能保持原文原貌,部分地方作了小幅調整以便更加符合中文思惟。文中圖片均來源於原文。水平有限,若有誤漏之處,還請讀者不吝賜教。最後但願此文能給讀者帶去幫助。

更多討論請到180251611

相關文章
相關標籤/搜索