一、reactnative布局采用flexbox布局
Flexbox是什么
根據規范中的描述可知道,Flexbox模塊提供了一個有效的布局方式,即使不知道視窗大小或者未知元素情況之下都可以智能的,靈活的調整和分配元素和空間兩者之關的關系。簡單的理解,就是可以自動調整,計算元素在容器空間中的大小。
這樣聽起來是不是太官方了,其實我也明白這種感覺。
如何開始使用Flexbox
這是每個人都會問的第一個問題,答案是比你預想的要簡單得多。
開始使用Flexbox時,你所要做的第一件事情就是聲明一個Flex容器(Flex Container)。
比如一個簡單的項目列表,我們常常看到的HTML形式如下所示:
<ul> <!--parent element--> <li></li> <!--first child element--> <li></li> <!--second child element--> <li></li> <!--third child element--> </ul>
一眼就能看出來,這就是一個無序列表(ul
)里有三個列表元素(li
)。
你可以把ul
稱為父元素,li
稱為子元素。
要開始使用Flexbox,必須先讓父元素變成一個Flex容器。
你可以在父元素中顯式的設置display:flex
或者display:inline-flex
。就這么的簡單,這樣你就可以開始使用Flexbox模塊。
實際是顯式聲明了Flex容器之后,一個Flexbox格式化上下文(Flexbox formatting context)就立即啟動了。
告訴你,它不是像你想像的那么復雜。
使用一個無序列表(ul
)和一群列表元素(li
),啟動Flexbox格式化上下文的方式如下:
/* 聲明父元素為flex容器 */ ul { display:flex; /*或者 inline-flex*/ }
給列表元素(li
)添加一點基本樣式,這里你可以看到發生了什么。
li { width: 100px; height: 100px; background-color: #8cacea; margin: 8px; }
你將看到的效果如下圖所示:
你可能沒有注意到,但事實上已經發生了變化。現在已經是一個Flexbox格式化上下文。
記住,默認情況下,div
在CSS中垂直堆棧的,也就是說從上到下排列顯示,就像下圖這樣:
上面的圖是你希望的結果。
然而,簡單的寫一行代碼display:flex
,你立即就可以看到布局改變了。
現在列表元素(li
)水平排列,從左到右。就像是你使用了float
一樣。
Flexbox模塊的開始,正如前面的介紹,在任何父元素上使用display:flex
。
你可能不明白為什么這一變化就能改變列表元素的排列方式。但我可以負責任的告訴你,你深入學習之后就能明白。現在你只需要信任就足夠了。
理解flex display
是使用Flexbox的一個開始。
還有一件事情,我需要提醒您注意。
一旦你顯式的設置了display
屬性的值為flex
,無序列表ul
就會自動變成Flex容器,而其子元素(在本例中是指列表元素li
)就變成了Flex項目。
這些術語會一次又一次的提到,我更希望你通過一些更有趣的東西來幫助你學習Flexbox模塊。
我使用了兩個關鍵詞,我們把重點放到他們身上。了解他們對於理解后面的知識至關重要。
- Flex容器(Flex Container):父元素顯式設置了
display:flex
- Flex項目(Flex Items):Flex容器內的子元素
這些只是Flexbox模塊的基礎。
Flex容器屬性
flex-direction || flex-wrap || flex-flow || justify-content || align-items || align-content
通過上面的內容,我們了解了一些基礎知識。知道了Flex容器和Flex項目是什么,以及如何啟動Flexbox模塊。
現在是一個好好利用它們的時間了。
有設置一個父元素作為一個Flex容器,幾個對齊屬性可以使用在Flex容器上。
正如你的塊元素的width
設置了200px
,有六種不同的屬性可以用於Flex容器。
好消息是,定義這些屬性不同於你以往使用過的任何一種方法。
flex-direction
flex-direction
屬性控制Flex項目沿着主軸(Main Axis)的排列方向。
它具有四個值:
/* ul 是一個flex容器 */ ul { flex-direction: row || column || row-reverse || column-reverse; }
簡單點來說,就是flex-direction
屬性讓你決定Flex項目如何排列。它可以是行(水平)、列(垂直)或者行和列的反向。
從技術上講,水平和垂直在Flex世界中不是什么方向(概念)。它們常常被稱為主軸(Main-Axis)和側軸(Cross-Axis)。默認設置如下所示。
通俗的說,感覺Main-Axis就是水平方向,從左到右,這也是默認方向。Cross-Axis是垂直方向,從上往下。
默認情況下,flex-direction
屬性的值是row
。它讓Flex項目沿着Main-Axis排列(從左向右,水平排列)。這就解釋了本文開始部分時無序列表的表現效果。
盡管flex-direction
屬性並沒有顯式的設置,但它的默認值是row
。Flex項目將沿着Main-Axis從左向右水平排列。
如果把flex-direction
的屬性值修改成column
,這時Flex項目將沿着Cross-Axis從上到下垂直排列。不再是從左向右排列。
flex-wrap
flex-wrap
屬性有三個屬性值:
ul { flex-wrap: wrap || nowrap || wrap-reverse; }
我將通過一個例子來解釋如何使用flex-wrap
屬性。首先在前面的無序列表的HTML結構中多添加幾個列表項li
。
將Flex容器設置適合大小以適合放置更多的列表項目或者說讓列表項目換行排列。這兩種方式,你是怎么想的?
<ul> <!--parent element--> <li></li> <!--first child element--> <li></li> <!--second child element--> <li></li> <!--third child element--> <li></li> <li></li> <li></li> </ul>
幸運的是,新添加的Flex項目剛好適合Flex容器大小。也就是Flex項目能剛好填充Flex容器。
再深入一點。
繼續給Flex容器內添加Flex項目,比如說添加到10
個Flex項目。這個時候會發生什么?
同樣的,Flex容器還是能容納所有的子元素(Flex項目)排列,即使瀏覽器出現了水平滾動條(當Flex容器中添加了很多個Flex項目,至使Flex容器的寬度大於視窗寬度)。
這是每一個Flex容器的默認行為。Flex容器會在一行內容納所有的Flex項目。這是因為flex-wrap
屬性的默認值是nowrap
。也就是說,Flex項目在Flex容器內不換行排列。
ul { flex-wrap: nowrap; /*Flex容器內的Flex項目不換行排列*/ }
no-wrap
不是不可改變的。我們可以改變。
當你希望Flex容器內的Flex項目達到一定數量時,能換行排列。當Flex容器中沒有足夠的空間放置Flex項目(Flex項目默認寬度),那么Flex項目將會換行排列。把它(flex-wrap
)的值設置為wrap
就有這種可能:
ul { flex-wrap: wrap; }
現在Flex項目在Flex容器中就會多行排列。
在這種情況下,當一行再不能包含所有列表項的默認寬度,他們就會多行排列。即使調整瀏覽器大小。
就是這樣子。注意:Flex項目現在顯示的寬度是他們的默認寬度。也沒有必要強迫一行有多少個Flex項目。
除此之外,還有一個值:wrap-reverse
。
是的,你猜對了。它讓Flex項目在容器中多行排列,只是方向是反的。
flex-flow
flex-flow
是flex-direction
和flex-wrap
兩個屬性的速記屬性。
你還記得使用border
的速記寫法?border: 1px solid red
。這里的概念是相同的,多個值寫在同一行,比如下面的示例:
ul { flex-flow: row wrap; }
相當於:
ul { flex-direction: row; flex-wrap: wrap; }
除了這個組合之外,你還可以嘗試一些其它的組合。flex-flow: row nowrap
,flex-flow: column wrap
,flex-flow: column nowrap
。
我相信你了解這些會產生什么樣的效果,要不嘗試一下。
justify-content
Flexbox模塊真得很好。如果你仍然不相信它的魅力,那么justify-content
屬性可能會說服你。
justify-content
屬性可以接受下面五個值之一:
ul {
justify-content: flex-start || flex-end || center || space-between || space-around }
justify-content
屬性又能給我們帶來什么呢?提醒你一下,你是否還記得text-align
屬性。其實justify-content
屬性主要定義了Flex項目在Main-Axis上的對齊方式。
來看一個簡單的例子,還是考慮下面這個簡單的無序列表:
<ul> <li>1</li> <li>2</li> <li>3</li> </ul>
添加一些基本樣式:
ul { display:flex; border: 1px solid red; padding: 0; list-style: none; background-color: #e8e8e9; } li { background-color: #8cacea; width: 100px; height: 100px; margin: 8px; padding: 4px; }
你將看到的效果是這樣:
通過justify-content
屬性,可以讓Flex項目在整個Main-Axis上按照我自己的欲望設置其對齊方式。
可能會有以下幾種類型。
flex-start
justify-content
的默認屬性值是flex-start
。
flex-start
讓所有Flex項目靠Main-Axis開始邊緣(左對齊)。
ul { justify-content: flex-start; }
flex-end
flex-end
讓所有Flex項目靠Main-Axis結束邊緣(右對齊)。
ul { justify-content: flex-end; }
center
和你預期的一樣,center
讓所有Flex項目排在Main-Axis中間(居中對齊)。
ul { justify-content: center; }
space-between
space-between
讓除了第一個和最一個Flex項目的兩者間間距相同(兩端對齊)。
ul { justify-content: space-between; }
你注意到有什么不同?看看下圖的描述:
space-around
最后,space-around
讓每個Flex項目具有相同的空間。
ul { justify-content: space-around; }
和space-between
有點不同,第一個Flex項目和最后一個Flex項目距Main-Axis開始邊緣和結束邊緣的的間距是其他相鄰Flex項目間距的一半。看看下圖的描述:
千萬不要覺得這些練習太多,這些練習可以幫助熟悉Flexbox屬性的語法。也能更好的幫助你更好的理解它們是如何影響Flex項目沿着Main-Axis的對齊方式。
align-items
align-items
屬性類似於justify-content
屬性。只有理解了justify-content
屬性,才能更好的理解這個屬性。
align-items
屬性可以接受這些屬性值:flex-start || flex-end || center || stretch || baseline
。
ul { align-items: flex-start || flex-end || center || stretch || baseline }
它主要用來控制Flex項目在Cross-Axis對齊方式。這也是align-items
和justify-content
兩個屬性之間的不同之處。
下面是不同的值對Flex項目產生的影響。不要忘記這些屬性只對Cross-Axis軸有影響。
stretch
align-items
的默認值是stretch
。讓所有的Flex項目高度和Flex容器高度一樣。
flex-start
正如你所希望的flex-start
讓所有Flex項目靠Cross-Axis開始邊緣(頂部對齊)。
flex-end
flex-end
讓所有Flex項目靠Cross-Axis結束邊緣(底部對齊)。
center
center
讓Flex項目在Cross-Axis中間(居中對齊)。
baseline
讓所有Flex項目在Cross-Axis上沿着他們自己的基線對齊。
結果看上去有點像flex-start
,但略有不同。那“baseline”到底是什么呢?下圖應該能幫助你更好的理解。
align-content
還記得前面討論的wrap
屬性嗎?我們在Flex容器中添加了更多的Flex項目。讓Flex容器中的Flex項目多行排列。
align-content
屬性用於多行的Flex容器。它也是用來控制Flex項目在Flex容器里的排列方式,排列效果和align-items
值一樣,但除了baseline
屬性值。
像align-items
屬性一樣,它的默認值是stretch
。你現在應該熟悉這些值。那它又是如何影響Flex容器里的10個Flex項目多行排列方式。
stretch
使用stretch
會拉伸Flex項目,讓他們沿着Cross-Axis適應Flex容器可用的空間。
你看到的Flex項目間的間距,是Flex項目自身設置的margin
值。
flex-start
之前你看到過flex-start
。這次是讓多行Flex項目靠Cross-Axis開始邊緣。沿着Cross-Axis從上到下排列。因此Flex項目在Flex容器中頂部對齊。
flex-end
flex-end
剛好和flex-start
相反,讓多行Flex項目靠着Cross-Axis結束位置。讓Flex項目沿着Cross-Axis從下到上排列,即底部對齊。
center
你猜到了,center
讓多行Flex項目在Cross-Axis中間。在Flex容器中居中對齊。
這是Flex容器的最后一個屬性。你現在知道如何使用各種Flex容器屬性。你可以在工作中實踐這些屬性。
Flex項目屬性
order || flex-grow || flex-shrink || flex-basis
在前一節中,我解釋了Flex容器及其對齊屬性。
確實漂亮。我想你也找到了感覺。現在我們把注意力從Flex容器轉移到Flex項目及其對齊屬性。
像Flex容器,對齊屬性也可以用在所有的Flex項目。那我們開始吧。
order
允許Flex項目在一個Flex容器中重新排序。基本上,你可以改變Flex項目的順序,從一個位置移動到另一個地方。
這不會影響源代碼。這也意味着Flex項目的位置在HTML源代碼中不需要改變。order
屬性的默認值是0
。它可以接受一個正值,也可以接受一個負值。
值得注意的是,Flex項目會根據order
值重新排序。從底到高。
要說明總得需要一個例子。考慮下面這個無序列表:
<ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> </ul>
默認情況下,所有Flex項目的order
值都是0
。把前面給列表的樣式運用過來。看到的效果如下:
Flex項目顯示是按HTML源代碼中的順序來顯示,Flex項目1
、2
、3
和4
。
如果因為某些原因,在不改變HTML文檔源碼情況之下,想把Flex項目一從1
變成最后。不能修改HTML文檔源碼意思是你不能把HTML代碼改成:
<ul> <li>2</li> <li>3</li> <li>4</li> <li>1</li> </ul>
這個時候就需要order
屬性。這個時候你需要把Flex項目一的order
值設置比其他Flex項目值更大。
如果你以前使用過z-index
屬性,那你就能更好的理解這個屬性。
li:nth-child(1){ order: 1; /*設置一個比0更大的值*/ }
Flex項目就重新進行了排列,從低到高排列。不要忘記了,默認情況下,Flex項目2、3、4的order
值為0
。現在Flex項目1的order
值為1
。
Flex項目2、3和4的order
值都是0
。HTML源代碼秩序並沒有修改過。如果給Flex項目2的order
設置為2
呢?
是的,你猜對了。它也增加堆棧。現在代表Flex項目的最高的order
值。
當兩個Flex項目具有相同的order
值呢?在下面的示例中,把Flex項目1和3設置相同的order
值。
li:nth-child(1) { order: 1; } li:nth-child(3) { order: 1; }
現在仍是從低到高排列。這次Flex項目3排在Flex項目1后面,那是因為在HTML文檔中Flex項目3出現在Flex項目1后面。
如果兩個以下Flex項目有相同的order
值時,Flex項目重新排序是基於HTML源文件的位置進行排序。這個屬性就不做過多的解釋。接下來繼續介紹其他的屬性。
flex-grow 和 flex-shrink
Flex項目最優秀的一點就是靈活性。flex-grow
和flex-shrink
屬性允許我們玩這個靈活性。
flex-grow
和flex-shrink
屬性控制Flex項目在容器有多余的空間如何放大(擴展),在沒有額外空間又如何縮小。
他們可能接受0
或者大於0
的任何正數。0 || positive number
。
接下來闡述它們的使用。使用一個簡單的無序列表做為例子,它只包含一個列表項。
<ul> <li>I am a simple list</li> </ul> ul { display: flex; }
添加更多的樣式,看起來像這樣:
默認情況下,flex-grow
屬性值設置為0
。表示Flex項目不會增長,填充Flex容器可用空間。取值為0
就是一個開和關的按鈕。表示flex-grow
開關是關閉的。
如果把flex-grow
的值設置為1
,會發生:
現在Flex項目擴展了,占據了Flex容器所有可用空間。也就是說開關打開了。如果你試着調整你瀏覽器的大小,Flex項目也會縮小,以適應新的屏幕寬度。
為什么?默認情況下,flex-shrink
的值是1
,也就是說flex-shrink
開關也是打開的。
可以仔細看看flex-grow
和flex-shrink
屬性在各種情況下的效果,這樣能更好的幫助你理解。
flex-basis
記得前面我說過,Flex項目是當我沒有的。但我們也可以控制。
flex-basis
屬性可以指定Flex項目的初始大小。也就是flex-grow
和flex-shrink
屬性調整它的大小以適應Flex容器之前。
前面介紹的是非常生要的,所以我們需要花一點時間來加強對他們的理解。
flex-basis
默認的值是auto
。flex-basis
可以取任何用於width
屬性的任何值。比如 % || em || rem || px
等。
注意:如果flex-basis
屬性的值是0
時,也需要使用單位。即flex-basis: 0px
不能寫成flex-basis:0
。
這里同樣使用只有一個列表項的列表做為示例。
<ul> <li>I am a simple list</li> </ul> ul { display: flex } li { padding: 4px; /*some breathing space*/ }
默認情況,Flex項目的初始寬度由flex-basis
的默認值決定,即:flex-basis: auto
。Flex項目寬度的計算是基於內容的多少來自動計算(很明顯,加上了padding
值)。
這意味着,如果你增加Flex項目中的內容,它可以自動調整大小。
<ul> <li>I am a simple list AND I am a simple list</li> </ul>
然而,如果你想將Flex項目設置一個固定的寬度,你也可以這樣做:
li { flex-basis: 150px; }
現在Flex項目的寬度受到了限制,它的寬度是150px
。
它變得更加有趣。
flex速記
flex
是flex-grow
、flex-shrink
和flex-basis
三個屬性的速記(簡寫)。
在適當的時候,我建議你使用flex
,這樣比使用三個屬性方便。
li { flex: 0 1 auto; }
上面的代碼相當於:
li { flex-grow: 0; flex-shrink: 1; flex-basis: auto; }
注意它們之間的順序。flex-grow
第一,然后是flex-shrink
,最后是flex-basis
。縮寫成GSB,可以幫助你更好的記憶。
如果flex
屬性值中少一個值,會發生什么呢?
如果你只設置了flex-grow
和flex-shrink
值,flex-basis
可能是默認值0
。這就是所謂的絕對flex項目。只有當你設置了flex-basis
,你會得到一個相對flex項目。
/* 這是一個絕對的Flex項目 */ li { flex: 1 1; /*flex-basis 默認值為 0*/ } /* 這是一個相對的Flex項目 */ li { flex-basis: 200px; /* 只設置了flex-basis的值 */ }
我知道你在想什么。你肯定想知道相對和絕對的Flex項目是什么?我將在后面回答這個問題。你只需要再次盲目信任就足夠了。
讓我們看看一些非常有用的flex
值。
flex: 0 1 auto
li { flex: 0 1 auto; }
這相當於寫了flex
默認屬性值以及所有的Flex項目都是默認行為。
很容易理解這一點,首先看看flex-basis
屬性。flex-basis
設置為auto
,這意味着Flex項目的初始寬度計算是基於內容的大小。
明白了?
把注意力放到下一個屬性,flex-grow
設置為0
。這意味着flex-grow
不會改變Flex項目的初始寬度。也就是說,flex-grow
的開關是關閉的。
flex-grow
控制Flex項目的增長,如果其值設置為0
,Flex項目不會放大以適應屏幕(Flex容器大小)。
最后,flex-shrink
的值是1
。也就是說,Flex項目在必要時會縮小。
應用到Flex項目效果就是這樣子:
注意:Flex項目沒有增長(寬度)。如果有必要,如果調整瀏覽器(調小瀏覽器寬度),Flex項目會自動計算寬度。
flex: 0 0 auto
li { flex: 0 0 auto; }
這個相當於flex: none
。
還是老規矩:寬度是被自動計算,不過彈性項目不會伸展或者收縮(因為二者都被設置為零)。伸展和收縮開關都被關掉了。
它基本上是一個固定寬度的元素,其初始寬度是基於彈性項目中內容大小。
看看這個 flex 簡寫是如何影響兩個彈性項目的。一個彈性項目會比另一個容納更多內容。
應該注意到的第一件事情是,這兩個彈性項目的寬度是不同的。因為寬度是基於內容寬度而自動計算的,所以這是預料得到的。
試着縮放一下瀏覽器,你會注意到彈性項目不會收縮其寬度。它們從父元素中突出來了,要看到所有內容,必須橫向滾動瀏覽器。
不要着急,稍后我會展示如何處理這種怪異的行為。
在縮放瀏覽器時,彈性項目不會收縮,而是從彈性容器中突出來了。
flex: 1 1 auto
這與 flex: auto
項目相同。
還是按我前面立的規矩。即,自動計算初始化寬度,但是如果有必要,會伸展或者收縮以適應整個可用寬度。
伸展和收縮開關打開了,寬度自動被計算。
此時,項目會填滿可用空間,在縮放瀏覽器時也會隨之收縮。
flex: "positive number"
這里正數可以代表任何正數(沒有引號)。這與 flex: “正數” 1 0
相同。
flex: 2 1 0
與寫為 flex: 2
是一樣的,2
表示任何正數。
li { flex: 2 1 0; / *與 flex: 2相同 */ }
與前面我立的規矩一樣,即,將彈性項目的初始寬度設置為零(嗯?沒有寬度?),伸展項目以填滿可用空間,並且最后只要有可能就收縮項目。
彈性項目沒有寬度,那么寬度該如何計算呢?
這個時候 flex-grow
值就起作用了,它決定彈性項目變寬的程度。由它來負責沒有寬度的問題。
當有多個彈性項目,並且其初始寬度 flex-basis
被設置為基於零的任何值時,比如 0px
,使用這種 flex
簡寫更實用。
實際發生的是,彈性項目的寬度被根據 flex-grow
值的比例來計算。
考慮如下兩個列表項標記及 CSS:
<ul> <li>I am One</li> <li>I am Two</li> </ul> ul { display: flex; } /* 第一個彈性項目 */ li:nth-child(1) { flex: 2 1 0; /* 與寫成 flex: 2 相同*/ } /* 第二個彈性項目 */ li:nth-child(2){ flex: 1 1 0; background-color: #8cacea; }
記住設置 flex-grow : 1
,會讓彈性項目填滿可用空間。伸展開關打開了。
這里有兩個彈性項目。一個的 flex-grow
屬性值是 1
,另一個是 2
,那么會出現啥情況呢?
兩個項目上的伸展開關都打開了。不過,伸展度是不同的,1
和 2
。
二者都會填滿可用空間,不過是按比例的。
它是這樣工作的:前一個占 1/3
的可用空間,后一個占 2/3
的可用空間。
知道是我怎么得到這結果的么?
是根據基本的數學比例。"單項比例 / 總比例",我希望你沒有漏過這些數學課。
看到出現啥情況了么?
即使兩個彈性項目內容一樣大(近似),它們所占空間還是不同。寬度不是基於內容的大小,而是伸展值。一個是另一個的約兩倍。
有關於
flex-grow
、flex-basis
和flex-shrink
之間的詳細計算,可以點擊@Chris Wright寫的《Flexbox adventures》博文。譯文可以點擊這里。
align-self
align-self
屬性更進一步讓我們更好地控制彈性項目。
你已經看到 align-items
屬性是如何有助於整體對齊彈性容器內的所有彈性項目了。
如果想改變一個彈性項目沿着側軸的位置,而不影響相鄰的彈性項目,該怎么辦呢?
這是 align-self
屬性大展拳腳的地方了。
該屬性的取值可以是這些值之一:auto || flex-start || flex-end || center || baseline || stretch
。
li:first-of-type { align-self: auto || flex-start || flex-end || center || baseline || stretch }
這些值你已經熟悉過了,不過作為回顧,如下是它們如何影響特定目標項目。這里是容器內的第一個項目。目標彈性項目是紅色的。
flex-end
flex-end
將目標項目(Flex項目)對齊到Cross-Axis的末端。
center
center
將目標項目(Flex項目)對齊到Cross-Axis的中間。
stretch
stretch
會將目標項目拉伸,以沿着Cross-Axis填滿Flex容器的可用空間(Flex項目高度和Flex容器高度一樣)。
baseline
baseline
將目標項目沿着基線對齊。它與flex-start
的效果看起來是一樣的,不過我相信你理解了基線是什么。因為前面已經解釋過。
auto
auto
是將目標Flex項目的值設置為父元素的 align-items
值,或者如果該元素沒有父元素的話,就設置為 stretch
。
在下面的情況下,彈性容器的 align-items
值為 flex-start
。這會把所有彈性項目都對齊到Cross-Axis的開頭。目標Flex項目現在繼承了 flex-start
值,即父元素的 align-item
值。
如下是上面Flex項目的基礎樣式。這樣你可以對發生的事情理解得更好點。
ul { display: flex; border: 1px solid red; padding: 0; list-style: none; justify-content: space-between; align-items: flex-start; /* 影響所有彈性項目 */ min-height: 50%; background-color: #e8e8e9; } li { width: 100px; background-color: #8cacea; margin: 8px; font-size: 2rem; }
現在你差不多已經為有趣的部分做好准備了 :-)
絕對和相對Flex項目
前面了解了一些基本概念,但重要的是要澄清一些重要的概念。那絕對和相對Flex項目之間到底有啥區別呢?二者之間主要的區別在於間距及如何計算間距。
一個相對Flex項目內的間距是根據它的內容大小來計算的。而在絕對Flex項目中,只根據 flex
屬性來計算,而不是內容。
考慮如下的標記:
<ul> <li> This is just some random text to buttress the point being explained. Some more random text to buttress the point being explained. </li> <li>This is just a shorter random text.</li> </ul>
兩個列表項元素,一個比另一個的文本多得多。
加點樣式:
ul { display: flex; /*觸發彈性盒*/ } li { flex: auto; /*記住這與 flex: 1 1 auto; 相同*/ border: 2px solid red; margin: 2em; }
如下是結果:
如果你已經忘了的話,flex: 1 1 auto
是與 flex-grow: 1
、flex-shrink: 1
和 flex-basis: auto
相同的。
Flex項目的初始寬度是被自動計算的(flex-basis: auto
),然后會伸展以適應可用空間(flex-grow: 1
)。
當Flex項目因為被設置為 flex-basis: auto
,而導致寬度被自動計算時,是基於Flex項目內包含的內容的大小而計算。
上面示例中Flex項目的內容大小不相同。因此,Flex項目的大小就會不相等。
既然各個寬度開始就不是相等的(它是基於內容的),那么當項目伸展時,寬度也保持不相等。
上面示例中的Flex項目是相對Flex項目。
下面我們把Flex項目變成絕對的, 就是說這次它們的寬度是基於 flex
屬性,而不是內容的大小。一行代碼就可以出奇跡。
li { flex: 1 ; /*與 flex: 1 1 0 相同*/ }
效果如下:
這次看到兩個Flex項目的寬度相同了嗎?
Flex項目的初始寬度是零(flex-basis: 0
),並且它們會伸展以適應可用空間。當有兩到多個Flex項目的 flex-basis
取值為0
時,它們會基於 flex-grow
值共享可用空間。
這個之前就討論過了。現在寬度不會基於內容大小而計算,而是基於指定的 flex
屬性值來計算。這樣你就明白了吧。對么?
絕對Flex項目的寬度只基於 flex
屬性,而相對Flex項目的寬度基於內容大小。
Auto-margin 對齊
當心Flex項目上的 margin: auto
對齊。當在Flex項目上使用 margin: auto
時,事情看起來就很怪異了。
你需要理解會發生什么。它會導致不可預料的結果,不過我打算解釋解釋。
當在Flex項目上使用 margin: auto
時,值為 auto
的方向(左、右或者二者都是)會占據所有剩余空間。
這玩意有點難理解。下面我來說明一下。
考慮如下的導航欄標記以及 CSS 樣式:
<ul> <li>Branding</li> <li>Home</li> <li>Services</li> <li>About</li> <li>Contact</li> </ul> ul { display: flex; } li { flex: 0 0 auto; }
你可以看到如下的效果:
這里有幾件事情要注意:
flex-grow
值為設置為0
。這就解釋了為什么列表項不會伸展。- Flex項目向Main-Axis的開頭對齊(這是默認行為)。
- 由於項目被對齊到Main-Axis開頭,右邊就有一些多余的空間。看到了吧?
現在在第一個列表項(branding)上使用 margin: auto
,看看會出啥情況。
li:nth-child(1) { margin-right: auto; /*只應用到右外邊距*/ }
剛剛發生了什么?之前的剩余空間現在已經被分配到第一個Flex項目的右邊了。
還記得我前面說的話吧?當在Flex項目上使用 margin: auto
時,值為 auto
的方向(左、右或者二者都是)會占據所有剩余空間。
如果想讓一個Flex項目的兩邊都用自動外邊距對齊,該怎么辦呢?
/* 如果願意的話,也可以用 margin 簡寫來設置兩個邊 */ li:nth-child(1) { margin-left: auto; margin-right: auto }
現在空白被分配到Flex項目的兩邊了。
那么,這是不是對很酷的自動外邊距對齊的一種折衷方案呢?看起來是。如果沒注意的話,它也可能是受挫之源。當在一個Flex項目上使用自動外邊距(margin: auto
)時,justify-content
屬性就不起作用了。
例如,在上面的Flex容器上通過 justify-content
屬性,設置不同的對齊選項時,對布局沒有影響。
ul { justify-content: flex-end; }
Flexbox實戰
導航系統是每個網站或者應用程序的重要組成部分。這個世界上的每個網站都會有某種導航系統。
下面我們看看這些熱門網站,以及它們是如何實現其導航系統的。你看到Flexbox是如何幫助你更高效地創建這些布局嗎?
也仔細看看哪里會用得上自動外邊距特性。
Bootstrap導航
AirBnB PC端導航
Twitter PC端導航
建議你自己寫代碼。試着自己實現這些導航系統。現在你已經掌握了所需的所有知識。你所需要的是一點勇氣去開始寫。
下一節再見。但願在你已經完成了導航系統練習之后。
切換flex-direction會發生什么?
提醒一下:將會有一些奇怪的東東出現。
在入手學習Flexbox時,這個部分是最惱火的。我估計很多彈性世界的新手也會發現如此。
還記得我說過默認的Main-Axis方向是從左到右,Cross-Axis方向是從上到下吧?
好吧,現在你也可以改變這個方向。
正如在較早的小節中所描述的那樣,用 flex-direction: column
時,確實是這樣。
當用 flex-direction: column
時,Main-Axis和Cross-Axis會向如下所看到的那樣改變:
如果曾用英語寫過文字,那么你就知道英語是從左到右,從上到下來寫的。
Flexbox的默認Main-Axis和Cross-Axis也是采用同樣的方向。
不過,如果將 flex-direction
切換為 column
,它就不再遵循英語的范式,而是日語的范式!
是的,日語。
如果你用日語寫過文字,那么應該很熟悉了。(鄭重聲明,我從沒用過日語寫過文字)。
日文通常是從上到下寫的!沒那么怪,對吧?
這就解釋了為嘛這對英語寫作者可能有點惱火。
看看下面這個例子。標准無序列表(ul
),帶有 3
個列表項(li
)。不過這次我要改變一下flex-direction
。
<ul> <li></li> <li></li> <li></li> </ul> ul { display: flex; flex-direction: column; }
如下是方向變化之前的樣子:
如下是方向變化之后的樣子:
出啥事了?
現在文字是以日語風格寫的:沿Main-Axis從上到下。
我很樂意指出,你會發現一些有趣的事情。
你會看到項目的寬度填滿了空間,對吧?
如果在之前要變成這樣子,得處理 flex-basis
以及 flex-grow
屬性。
下面來看看這些會如何影響新的布局。
li { flex-basis: 100px; }
下面是你會得到的。
什么?高度是被影響了,但是寬度沒有啊?我之前說過,flex-basis
屬性定義每個Flex項目的初始寬度。
我是錯的,或者這樣說更好:我是用英語來思考。下面我們姑且切換到用日語思考。並且總是得有寬度。
在切換 flex-direction
時,請注意,影響Main-Axis的每一個屬性現在會影響新Main-Axis。像 flex-basis
這種會影響Main-Axis上Flex項目寬度的屬性,現在會影響項目的高度,而不是寬度。
方向已經被切換了!
所以,即使你使用 flex-grow
屬性,它也是影響高度。本質上,每個作用於橫向軸(即Main-Axis)上的 flex 屬性,現在都會作用於縱向上的新Main-Axis。它只是在方向上的一個切換。
這里再來一個例子。我發誓在這個例子之后你會有更好的理解。減少之前看到過的Flex項目的寬度,它們就不再填滿整個空間了:
li { width: 200px; }
如果想把列表項移到屏幕中間該怎么辦呢?
在英語中,這是你到目前為止處理彈性容器的方式。就是說, 把Flex項目移到Main-Axis的中間 。
所以,你會用 justify-content: center
。但是這樣做不起作用。因為方向變了,中心是沿着Cross-Axis,而不是Main-Axis。
再來看看:
所以請用日語文字來思考。Main-Axis是從上到下,你不需要這樣。Cross-Axis是從左到右。貌似是你所要的。
你需要 把Flex項目移到Cross-Axis的中間 。這里想起哪個Flex容器屬性了么?
是的,align-items
屬性。align-items
屬性處理Cross-Axis上的對齊。
所以要把這些項目移到中間,得這樣做:
li { align-items: center; }
瞧瞧!Flex項目已經居中了吧。
是有點懸乎,我知道。如果需要,就再復習一遍好了。在研究Flexbox時,我發現很多 CSS 書都略過了這一部分。
用日語文字來思考一下會有很大幫助。我們有必要了解,所有Flexbox屬性都是基於合適的 flex-direction
起作用。
相信你又學到了一點新東西。我很開心解釋這些,希望你也很開心。
我的天啦,彈性盒解決了那問題了?
很多設計師用 CSS 時遇到的一些典型問題已經被Flexbox輕而易舉解決了。
@Philip Walton 在其 Flexbox解決了的問題這個項目 列出了 6 種典型的問題(到本文編寫時)。
他全面討論了之前用 CSS 的限制,以及目前Flexbox提供的解決方案。建議在完成本文后看一看。
在接下來的實踐環節中,在開始用彈性盒創建一個音樂應用布局時,我會解釋他提出的一些概念。
Flex 不兼容瀏覽器的坑
如果你不是在夢中寫 CSS 的那一類人的話,就可能想看看這個 Github 倉庫。
有些比我聰明的人在這里維護了一個Flexbox的 bug 列表及其變通方法。當有些事情沒有按預期起作用時,這是我看的第一個地方。我也會在后面的實踐環節中帶你踩踩一些顯眼的坑。
@Philip Walton早前專門寫了一篇有關於解決Flexbox跨瀏覽器兼容Bug的文章《Normalizing Cross-browser Flexbox Bugs》,對應的譯文可以點擊這里閱讀。
用彈性盒創建一個音樂應用的布局
在學習完了乏味嚴謹的基礎知識之后,該有一些有趣的項目了。是時候玩玩實際的例子,並把剛獲得的Flexbox技能用上去。
想出一個好項目花了我好幾天。由於缺乏創造性的選擇,我想出了一個貓玩的音樂應用布局。我稱它為 catty music 。
也許到了 2036 年,我們就能讓貓在火星上的某個地方唱搖滾。如下是完成了的布局看起來的樣子,並且完全是用Flexbox布局的。
可以在線上看,在這里。
如果在移動設備上看,外觀會稍微有點不同。這是在本文響應式設計一節中要解決的問題。
不過,有件事我得坦白。我已經做了許多人認為是錯誤的事情。我完全用Flexbox創建整個布局。
出於多種理由,這樣做可能並非那么理想。但是在這種情況下是故意的。我只是想用一個項目,向你展示所有可以用Flexbox做的事情。
如果你好奇什么時候使用Flexbox是對的,什么時候是錯的,那你可以讀讀我寫的一篇文章。
Flexbox 很棒,不過它在這里不受歡迎 Flexbox 毋庸置疑是我們大多數人遇到的最好的東西(如果你寫 CSS 的話),不過它是不是適合所有情況呢…
這里,我心里的石頭終於落地了。現在我相信在讀完這個之后,沒人會對我大呼小叫了。
Catty Musci 中的一切都是用Flexbox布局, 這是故意盡可能地炫技。
所以下面我們開始把這個玩意建成!對於任何稍微合理一點的項目來說,有點規划對避免效率低下是有幫助的。就讓我帶你看看創建 Catty Music 布局的規划方法。
從哪里開始?
只要用Flexbox創建布局,就應該從找出布局中哪一個部分會作為Flex容器開始。然后才可以使用Flexbox提供的強大對齊屬性。
分析
你可以讓整個包含體作為Flex容器(下圖中被包含在紅色邊框內的部分),並把布局的其它部分分成Flex項目(Item 1
和 Item 2
)。
這樣做完全說得通,讓 Item 1
包含布局中除了腳注以外的每個部分。腳注部分用來包含音樂控制按鈕。
你知道Flex項目也可以成為Flex容器嗎?是的,是可能的!
你想嵌套多深就嵌套多深(不過理智的做法是保持一個合理的水平)。於是,根據這個新啟示就有了這個...
Item 1
(第一個Flex項目)也可以弄成一個Flex容器。然后,側邊欄(Item 1b
)和主欄目(Item 1a
)就成了 Item 1
的Flex項目。
你依然與我同在,對吧?像這樣拆分布局,會給你一個相當不錯的心理模型來處理。
在用Flexbox開始創建更為復雜的布局時,你會看到這有多重要。當然,你並不需要一個像上面那樣高大上的圖像。在紙上畫一個簡單的草圖就足夠了。
記得我說過可以想嵌套多深就嵌套多深吧?貌似這里還可以再來一個嵌套。
看看上面的主欄目(Item 1a
)。它也可以變成一個Flex容器,以容納如下高亮度的部分:Item 1a — A
和 Item 1a — B
。
可能你會決定不把主欄目(Item 1a
)變成Flex容器,只在其內部放入兩個 div
來容納高亮度部分。
是的,這樣做沒問題,因為 “Item 1a — A” 和 “Item 1a — B” 是垂直堆放的。
默認情況下,div
是垂直堆放的。這是盒模型的工作原理。如果選擇把主欄目變成一個Flex容器,就有了隨時可你供調遣的強大對齊屬性。
Flexbox中的 Flex 是指彈性、靈活。Flex容器默認是彈性的,跟響應式有點類似。這也許是使用Flex容器,而不是普通 div
的另一個原因。不過,這取決於實際情況。
在你創建 Catty Music 時我會論及一些事情事情。你現在應該去寫點代碼了。
HTML結構
從如下的基礎 HTML 設置開始。
<!DOCTYPE html> <html> <head> <title>Catty Music</title> </head> <body> <main></main> <!--用來包含應用的主欄目--> <footer></footer> <!--用來包含音樂控制按鈕和歌曲細節--> </body> </html>
然后為它設置樣式:
html, body { height: 100%; /*顯式設置這,是很重要的*/ } body { display: flex; /*flex 超能力被激活了! */ flex-direction: column; /*垂直而不是水平堆放彈性項目(主欄目和腳注元素)*/ }
使用Flexbox的第一個步驟就是確定一個Flex容器。
這正好是上面代碼所做的。它將 body
元素的 display
屬性設置為 flex
。現在有了一個Flex容器,即 body
元素。
Flex項目也被定義了(item 1
和 item 2
):跟前面分析中所做的一樣。
注意,如果你對這個概念還是有點模糊,就應該再看看我在之前開始分析時展示的圖像。
盯着最后的圖像,你應該讓彈性項目工作起來。
讓腳注吸附在底部。讓放音樂控制的腳注吸附在頁面的底部,同時讓主欄目填滿剩余空間。
你怎么實現?
main { flex: 1 0 auto; /*填滿剩余空間*/ } footer { flex: 0 0 90px; /*不會放大或者收縮 - 只會固定在 90px 高度。*/ }
請看上面列出的代碼中的注釋。多虧了 flex-grow
屬性,它讓主欄目填滿整個空間相對很容易。只需將 flex-grow
屬性的值設置為 1
即可。還應該把 flex-shrink
屬性設置為 0
。為什么呢?
因為 flex-direction
沒有改變,所以原因也許不很明顯。
在有些瀏覽器中,會有一個 bug,允許Flex項目收縮后比其內容尺寸小。這是個很古怪的行為。
解決這個 bug 的變通方案是把 flex-shrink
的屬性值設置為 0
,而不是默認值 1
,同時,把 flex-basis
屬性設置為 auto
。
就像是說:請自動計算Flex項目的大小,但是不要收縮。有了這個簡寫值,就可以得到Flex項目的默認行為。
Flex項目會隨着瀏覽器縮放那個收縮。縮放不是基於 flex-shrink
屬性,而是基於自動對Flex項目的寬度重新計算(flex-basis: auto
)。
這會導致Flex項目至少與它的寬度或者高度(如果聲明了)或者默認內容尺寸一樣大。請不要忘記我在分析 flex
簡寫屬性時設立的原則。后面會有更多簡寫的東西。
現在事情匯集到一起了,下面我們放點樣式來定義間距和顏色。
body { display: flex; flex-direction: column; background-color: #fff; margin: 0; font-family: Lato, sans-serif; color: #222; font-size: 0.9em; } footer { flex: 0 0 90px; padding: 10px; color: #fff; background-color: rgba(61, 100, 158, .9); }
依然沒有啥奇跡。你將看到的效果如下圖所示:
看看事情是如何開始初具規模,你會讓它變得更好一點。
固定側邊欄
如果你是跟着寫代碼,就更新一下你的 HTML 文檔。
<main> <aside> <!--這代表側邊欄,其中包含了來自 font-awesome 的圖標集--> <i class="fa fa-bars"></i> <i class="fa fa-home"></i> <i class="fa fa-search"></i> <i class="fa fa-volume-up"></i> <i class="fa fa-user"></i> <i class="fa fa-spotify"></i> <i class="fa fa-cog"></i> <i class="fa fa-soundcloud"></i> </aside> <section class="content"> <!--這一部分會容納除側邊欄以外的所有東西--> </section> </main>
上面的代碼列表已經解釋的很清楚了。
至於圖標設置,我用了熱門的 Font Awesome 庫。這樣用你想要的圖標就簡單到只需添加一個 CSS 類即可。這就是我在 aside
標記中所做的事情。
正如之前解釋過的,上面的 main
部分也會成為一個Flex容器。側邊欄(用 aside
標記表示) 以及 section
會成為Flex項目。
main { flex: 1 0 auto; /* 變成一個彈性項目*/ display: flex; /*只包含這一行,現在就有一個彈性容器,帶有彈性項目:側邊欄和主內容區*/ }
很好,越來越有意思了,嘿嘿。
現在,主欄目是一個Flex容器了。下面我們來處理它的Flex項目之一,側邊欄。跟讓腳注吸附到頁面底部一樣,你還會想讓側邊欄吸附到頁面的左邊。
aside { flex: 0 0 40px; /*不會放大或者縮小。固定在 40px*/ }
側邊欄應該讓圖標垂直堆放。可以讓側邊欄變成Flex容器,給它設一個 flex-direction
,讓所有圖標垂直堆放。然后應用一個對齊屬性,讓圖標就位。
在下面的代碼列表中,看看你可能會怎么做。
aside { /* ... */ display: flex; /*現在也是一個彈性容器*/ flex-direction: column; /*垂直堆放圖標*/ /*因為方向改變了,如下在垂直方向上起作用。*/ justify-content: space-around; align-items: center; /*方向改變了!這條影響的是水平方向。將圖標居中*/ background-color: #f2f2f2; /*讓我變漂亮*/ } aside i.fa { font-size: 0.9em; /*圖標的字體大小*/ }
我已經在上面的代碼中加了很多注釋,現在看看布局是如何漂亮。很干凈,只有幾行代碼。合情合理的代碼,沒有亂七八糟的招數。
主內容區目前是空的。不要忘記它是第二個列表項,側邊欄是第一個。給這里放一下東西。給主內容區添加內容你可以再看看完工的項目,這樣就不會忘記接下來要發生的事情。
更重要的是,這能幫助你理解下面的代碼。更新 HTML 文檔,在 .content
部分添加如下標記:
<section class="content"> <!--這一區是空的,用內容填充它--> <div class="music-head"> <!--第一個列表項:包含音樂詳情--> <img src="images/cattyboard.jpg" /><!--專輯封面--> <section class="catty-music"> <!--專輯的其它細節--> <div> <p>CattyBoard Top 100 Single Charts (11.06.36)</p> <p>Unknown Artist</p> <p>2016 . Charts . 100 songs</p> </div> <div> <!--Music controls--> <i class="fa fa-play">Play all</i> <i class="fa fa-plus">Add to</i> <i class="fa fa-ellipsis-h">More</i> </div> </section> </div> <!--end .music-head--> <!--第二個列表項:包含專輯中的歌曲列表--> <ul class="music-list"> <li> <p>1. One Dance</p> <p>Crake feat CatKid & Cyla</p> <p>2:54</p> <p><span class="catty-cloud">CATTY CLOUD SYNC</span></p> </li> <li> <p>2. Panda</p> <p>Cattee</p> <p>4:06</p> <p><span class="catty-cloud">CATTY CLOUD SYNC</span></p> </li> <li> <p>3. Can't Stop the Feeling!</p> <p>Catin Cimberlake</p> <p>3:56</p> <p><span class="catty-cloud">CATTY CLOUD SYNC</span></p> </li> <li> <p>4. Work From Home</p> <p>Cat Harmony feat Colla</p> <p>3:34</