彈性佈局flex是一個幾年前的CSS屬性了,說它解放了一部分生產力不為過。至少解放了不少CSS佈局相關的面試題 :) 之前網上流行的各種XX佈局,什麼postion: absolute+margin,float+padding,各種都可以使用flex來取代之。 早兩年在使用的時候,還是會擔心有相容性 ...
彈性佈局flex
是一個幾年前的CSS屬性了,說它解放了一部分生產力不為過。至少解放了不少CSS佈局相關的面試題 :)
之前網上流行的各種XX佈局,什麼postion: absolute
+margin
,float
+padding
,各種都可以使用flex
來取代之。
早兩年在使用的時候,還是會擔心有相容性問題的,某些手機在使用了auto-prefixer
以後依然會出現不相容的問題。
好在現在已經是2018年了,不必再擔心那些老舊的設備,希望這篇文章能幫你加深對flex
的認識。
準備工作
首先,flex
被稱為一個彈性盒模型,也有稱彈性佈局的。
總之,盒子也好、佈局也罷,我們總是需要有一個容器Container
的:
<div class="container"></div>
以及如果單純的只是一個容器的話,是沒有任何意義的。
所以我們還需要有一些內容:
<div class="contianer"> <div class="item"></div> <div class="item"></div> </div>
下邊的所有例子基本都是基於以上DOM結構來做的。
基本語法
現在我們已經有一個可以用來寫flex
佈局的html
結構。
接下來就是一個最基礎的flex
佈局的實現:
<style> .container { display: flex; height: 50px; color: #fff; border: 1px solid #03a9f4; } .item { flex: 1; text-align: center; background: #03a9f4; } </style> <div class="contianer"> <div class="item"></div> <div class="item"></div> </div>
我們在容器上使用display: flex
來告訴瀏覽器,這是一個flex
佈局的開始。
然後給所有的item
添加一個flex: 1
的屬性,來表明,我們這裡邊的元素都是flex
佈局中的內容,
我們會沿著主軸來平分所有的區域,就這樣,我們已經實現了一個多列等寬佈局。
關於flex
,最重要的就是要記住他有兩條軸線(主軸、交叉軸),絕大部分屬性都是依賴於軸線的方向。
圖片來自MDN
因為flex
佈局分為了容器和內容兩塊,各自有各自的屬性,所以就先從容器類的說起。
容器相關的flex屬性
實現上邊的需求,是依賴於很多預設屬性值。
比如,為什麼我們的子元素會橫向的進行分割空間,而不是豎向的,這裡就用到了一個屬性的預設值:
flex-direction
flex-direction
用於定義flex
佈局中的主軸方向。
預設取值為row
,是橫向的,表示從左到右,也就是說我們的所有子元素會按照從左到右的順序進行排列。
我們可以通過設置值為column
來改變主軸的方向,將其修改為從上到下。(改變flex-direction
的值會影響到一些相關的屬性,會在下邊說到)
flex-direction
共有四個有效值可選:
row
預設值,從左到右row-reverse
從右到左column
從上到下column-reverse
從下到上
P.S. 在React-Native中預設的主軸方向為column
所以說flex-direction
的作用就是:定義容器中元素的排列方向
flex-wrap
該屬性用於定義當子元素沿著主軸超出容器範圍後,應該按照怎樣的規則進行排列。
該屬性只有簡單的三個取值:
wrap
超出主軸範圍後換行顯示,換行方向按照交叉軸的方向來(預設情況下就是折行到下一行)wrap-reverse
超出主軸範圍後換行顯示,但是方向是交叉軸的反向(也就是預設情況下第一行會出現在最下邊)nowrap
即使超出容器也不會進行換行,而是嘗試壓縮內部flex元素的寬度(在下邊的子元素相關的屬性會講到)
三種取值的示例:
flex-flow
flex-flow
是一個簡寫的屬性,適用於上邊提到的flex-direction
和flex-wrap
語法:
selector { flex-flow: <flex-direction> <flex-wrap>; }
justify-content
這個會定義我們的子元素如何沿著主軸進行排列,因為我們上邊是直接填充滿了父元素,不太能看出效果。
所以我們對代碼進行如下修改:
<style media="screen"> .container { display: flex; width: 400px; color: #fff; border: 1px solid #03a9f4; } .item { /* flex: 1; */ width: 100px; text-align: center; background: #03a9f4; } </style> <div class="container"> <div class="item">Item 1</div> <div class="item">Item 2</div> <div class="item">Item 3</div> </div>
將所有的子元素都改為固定的寬度,也就是說,如果父元素有剩餘空間的話,就會空在那裡。
justify-content
的預設取值為normal
,也可以認為就是start
了,也就是根據主軸的方向(flex-direction
)堆在起始處。
幾個常用的取值:
center
必然首選的是center
,能夠完美的實現沿主軸居中flex-start
沿著主軸從行首開始排列flex-end
沿著主軸從行末開始排列
以及幾個不太常用的取值:
space-between
將剩餘空間在子元素中間進行平分,保證沿主軸兩側不會留有空白。space-around
將剩餘空間均勻的分佈在所有的子元素沿主軸方向的兩側,也就是說,主軸兩側也會有空白,但是必然是中間空白的1/2
大小。space-evenly
將剩餘空間在所有元素之間平均分配,主軸兩側的空白麵積也會與中間的面積相等。
六種效果的示例:
Warning
有一點需要註意,justify-content
的取值都是依照flex-direction
所定義的主軸方向來展示的。
也就是說,center
在預設情況下用於水平居中,在flex-direction: column-*
時,則是作為垂直居中來展示的。
align-content
同樣的,align-content
也是用來控制元素在交叉軸上的排列順序,但是既然會出現兩個屬性(align-items
和align-content
),勢必兩者之間會有一些區別。
因為align-content
只能作用於多行情況下的flex
佈局,所以取值會更接近額旋轉後的justify-content
,同樣的可以使用space-between
之類的屬性值。
因為取值基本類似,所以不再重覆上邊justify-content
所列的表格,直接上效果:
align-items
align-items
與上邊的justify-content
類似,也適用於定義子元素的排列方式。
不同的是,align-items
作用於交叉軸(也就是預設flex-direction: row
情況下的從上到下的那根軸線)
目測平時用到的最多的地方就是水平居中吧(我現在懶的:只要有圖標、表單 & 文字 的單行混合,都會選擇align-items: center
來實現:))
常用的取值:
center
常用來做垂直(交叉軸)居中flex-start
沿著交叉軸的起始位置排列flex-end
與flex-start
方向相反stretch
將元素撐滿容器的交叉軸寬度(在預設情況下,這裡指容器的高度,但是如果單純的說這條軸線,我覺得寬度更合適一些)baseline
將元素按照文本內容的基線進行排列
以上取值的示例:
align-content與align-items的異同
兩者的相同點在於,都是設置元素在交叉軸上的排列順序。
而區別在於以下兩點:
align-content
只能應用於多行的情況下align-content
會將所有的元素認為是一個整體併進行相應的處理、而align-items
則會按照每一行進行處理:
place-content
place-content
可以認為是justify-content
和align-content
的簡寫了(事實上就是)
語法為:
selector { place-content: <align-content> <justify-content>; }
P.S. 如果單行(元素)想要實現居中還是老老實實的使用align-items
+justify-content
吧 :)
子元素的屬性們
有關容器的所有屬性都已經列在了上邊,下邊的一些則是在容器內元素設置的屬性。
flex-grow
flex-grow
用來控制某個子元素在需要沿主軸填充時所占的比例,取值為正數(浮點數也可以的)。
selector { flex-grow: 1; flex-grow: 1.5; }
舉例說明:
如果一個容器中有三個元素,容器剩餘寬度為100px,三個元素需要進行填充它。
如果其中一個元素設置了flex-grow: 2
,而其他的設置為1
(預設不設置的話,不會去填充剩餘寬度)
則會出現這麼一個情況,第一個元素占用50px
,而其他兩個元素各占用25px
。
Warning
這裡需要註意的一點是,flex-grow
定義的是對於剩餘寬度的利用。
元素自身占用的空間不被計算在內,為了驗證上邊的觀點,我們進行一個小實驗。
給每一個元素設置一個padding-left: 20px
,保證元素自身占用20px
的位置,然後分別設置flex-grow
來查看最後元素的寬度是多少。
.container { display: flex; width: 160px; height: 20px; align-items: stretch; } .item { flex-grow: 1; padding-left: 20px; } .item:first-of-type { flex-grow: 2; }
我們給容器設置了寬度為160px
(為了方便的減去padding-left
計算)。
最後得到的結果,設置了flex-grow: 2
的元素寬度為70px
,而設置flex-grow: 1
的元素寬度為45px
。
在減去了自身的20px
以後,50 / 25 === 2 // true
。
flex-shrink
flex-shrink
可以認為是與flex-grow
相反的一個設置,取值同樣是正數。
用來設置當容器寬度小於所有子元素所占用寬度時的縮放比例。
比如說,如果我們的容器寬為100px
,三個元素均為40px
,則會出現容器無法完全展示所有子元素的問題。
所以預設的flex
會對子元素進行縮放,各個元素要縮放多少,則是根據flex-shrink
的配置來得到的(預設為1,所有元素平均分攤)
就像上邊的例子,如果我們還是三個元素,第一個設置了flex-shrink: 2
,則最終得到的結果,第一個元素寬度為30px
,其餘兩個元素的寬度為35px
。
.container { display: flex; width: 100px; height: 20px; align-items: stretch; } .item { width: 40px; /* flex-shrink: 1; it's default value */ font-size: 0; background: #03a9f4; } .item:first-of-type { flex-shrink: 2; }
flex-basis
這個屬性用來設置元素在flex
容器中所占據的寬度(預設主軸方向),這個屬性主要是用來讓flex
來計算容器是否還有剩餘面積的。
預設取值為auto
,則意味著繼承width
(direction: column
時是height
)的值。
一般來講很少會去設置這個值。
flex
flex
則是上邊三個屬性的簡寫,語法如下:
selector { flex: <flex-grow> <flex-shrink> <flex-basis>; }
一般來講如果要寫簡寫的話,第三個會選擇設置為auto
,也就是獲取元素的width
。
align-self
效果如同其名字,針對某一個元素設置類似align-items
的效果。
取值與align-items
一致,比如我們可以針對性的實現這樣的效果:
.center :first-child { align-self: stretch; } .flex-start :first-child { align-self: flex-end; } .flex-end :first-child { align-self: flex-start; } .stretch :first-child { align-self: center; }
order
以及最後這裡還有一個order
屬性,可以設置在展示上的元素順序。
取值為一個任意整數。
預設的取值為1
,如果我們想要後邊的元素提前顯示,可以設置如下屬性:
.item:last-of-type { order: -1; }
P.S. 這個順序的改變只是顯示上的,不會真正的改變html的結構,比如,你依然不能通過.item:last-of-type ~ .item
來獲取它在視覺上後邊的兄弟元素
當order重覆時,按照之前的順序排列大小
總結
flex
相關的屬性如何拆分以後,並不算太多。
腦海中有主軸和交叉軸的概念之後,應該會變得清晰一些。
關於上述所有屬性的一個簡單總結:
容器相關
屬性名 | 作用 |
---|---|
flex-direction |
用來設置主軸的方向,最基礎的屬性,預設從左到右,此屬性一改,下列所有的屬性都要跟著改,真可謂牽一發而動全身 |
flex-wrap |
設置元素超出容器後的換行規則,預設不換行 |
justify-content |
設置沿主軸的排列規則 |
align-content |
設置沿交叉軸的排列規則 |
align-items |
以行(預設direction 情況下)為單位,設置沿交叉軸的排列規則 |
元素相關
屬性名 | 作用 |
---|---|
flex-grow |
當容器大於所有元素時,按什麼比例將剩餘空間分配給元素 |
flex-shrink |
當容器小於所有元素時,元素按照什麼比例來縮小自己 |
flex-basis |
很少用的屬性,設置在容器中的寬(高) |
align-self |
針對某些元素單獨設置align-items 相關的效果 |
order |
設置元素在顯示上的順序 |
簡寫
屬性名 | 作用 |
---|---|
flex-flow |
flex-direction 與flex-wrap 的簡寫 |
place-content |
justify-content 與align-content 的簡寫 |
flex |
flex-grow 、flex-shrink 與flex-basis 的簡寫 |
以及文中所有的示例代碼都在這裡 code here。
參考資料
- How Flexbox works (此文中的一些gif圖真心贊)
- Understanding Flexbox: Everything you need to know
- Learn CSS Flexbox in 5 Minutes