Flex布局完全指南
原文:A Complete Guide to Flexbox
作者:CHRIS COYIER 譯者:Shelley Lee 轉載需提前聯繫譯者,未經允許不得轉載。
背景介紹
Flexbox 布局(也叫Flex布局,彈性盒子布局)目標在於提供一個更有效地布局、對齊方式,並且能夠使父元素在子元素的大小未知或動態變化情況下仍然能夠分配好子元素之間的間隙。
Flex布局的主要思想是使父元素能夠調節子元素的高度、寬度和排布的順序,從而能夠最好地適應可用布局空間(能夠適應不同的設備和不同大小的屏幕)。設定為flex布局的元素能夠放大子元素使之儘可能填充可用空間,也可以收縮子元素使之不溢出。
最重要的是,與傳統布局中塊狀元素按照垂直方向擺放,行內元素按照水平方向擺放相比,flex布局是無方向的。傳統布局在應對大型複雜的布局時缺乏靈活性,特別是在改變方向、改變大小、伸展、收縮等等方面。
注: Flex 布局比較適合小規模的布局,Gird布局面向更大規模的布局。
基本概念
Flex布局是一個完整的模塊而不是一個單獨的屬性,它包括了完整的一套屬性。其中有的屬性是設置在容器(container,也可以叫做父元素,稱為flex container)上,有的則是設置在容器的項目上(item,也可以叫做子元素,稱為flex items)上。
譯者註:由於item譯成項目不夠直觀和形象,以下統一用父元素指代container,子元素指代item。
如果我們可以說傳統布局是建立在塊狀元素垂直流和行內元素水平流上的,那麼flex布局就是建立在」flex-flow方向」上的,通過下圖解釋flex布局的主要思想。
在flex布局中,子元素要麼按照主軸也就是main axis(從main-start到main-end)排布,要麼按照交叉軸,也就是cross axis(從cross-start到cross-end)排布。
下面介紹幾個概念:
- main axis: Flex 父元素的主軸是指子元素布局的主要方向軸,注意主軸不一定是水平的,它由屬性flex-direction來確定主軸是水平還是垂直的(後面會介紹)。
- main-start|main-end: 分別表示主軸的開始和結束,子元素在父元素中會沿著主軸從main-start到main-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-flow是flex-direction和flex-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-height、max-height、height的情況下拉伸子元素使之填充整個父元素。
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-items和align-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
flex是flex-grow、flex-shrink、flex-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,clear和vertical-align對flex子元素沒有任何影響。
示例
示例一:水平垂直居中
我們從一個非常非常簡單的例子開始,解決一個我們經常會遇到的問題:水平垂直居中。如果使用flex布局會非常簡單。
.parent { display: flex; height: 300px; /* 隨意設定大小 */}.child { width: 100px; /* 隨意設定大小,比父元素要小 */ height: 100px; /* 同上 */ margin: auto; /* 見證奇蹟的時刻 */}
這個主要原因是,在flex布局的父元素中設置margin為auto會自動吸收額外的空間,所以設置水平垂直的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);}
其他資源
- Flexbox in the CSS specifications
- Flexbox at MDN
- Flexbox at Opera
- Diving into Flexbox by Bocoup
- Mixing syntaxes for best browser support on CSS-Tricks
- Flexbox by Raphael Goetter (FR)
- Flexplorer by Bennett Feely
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;)
更多混合使用語法達到最佳瀏覽器兼容,可以參考 this article (CSS-Tricks)](Using Flexbox: Mixing Old and New for the Best Browser Support | CSS-Tricks)或者this article (DevOpera)
譯者的話
網上有不少flex相關教程,但當我看到CHRIS COYIER的這篇文章時,不禁被其詳盡所震撼,最近也在撰寫布局相關的文章,故產生了翻譯此文的想法。翻譯過程中盡量保持原文原貌,部分地方做了小幅調整以便更加符合中文思維。文中圖片均來源於原文。水平有限,如有誤漏之處,還請讀者不吝賜教。最後希望此文能給讀者帶去幫助。
更多交流請到180251611
推薦閱讀:
※Vue.js 實用技巧(二)
※極樂技術周報(第二十八期)
※Web Components 在 GitHub 中的應用
※天啦嚕!原來Chrome自帶的開發者工具還能這麼用!