Flexbox佈局的正確使用姿式

在項目中,咱們還會大量使用到flexbox的新舊屬性,但大多數人通常只會寫新屬性,舊屬性交由autoprefixer處理,但其實完成一樣功能的新舊屬性表現形式卻不盡相同。還有部分人只使用「萬能」的flex:number屬性爲伸縮項目分配空間,但有些特殊情景卻沒法知足,此文爲此梳理了flexbox的新舊屬性區別和分配空間的原理,爲你們用flexbox佈局的項目統統渠。chrome

Flexbox兼容性

PC端的兼容性
flexbox-pcsegmentfault

移動端的兼容性
flexbox-wap佈局

如上圖,爲了兼容IE10-11和Android4.3-,UC,咱們仍須要使用Flexbox的舊屬性。flex

Flexbox新舊屬性

Flexbox的新屬性提供了不少舊版本沒有的功能,可是目前Android4.x和UC仍有必定市場佔有率須要兼容,所以目前只使用新舊屬性都有的功能。
能實現相同功能的Flexbox新舊屬性以下表:
flexbox-attributeflexbox

但想象是美好的,現實是殘酷的,新舊屬性裏有那麼幾個頑固分子並不能乖乖的表現的同樣,總有那麼一點不一樣。
下面咱們來看看是哪些新舊屬性有不一樣:spa

flex-direction:row-reverse vs box-orient:horizontal;box-direction:reverse

相同點:改變主軸方向和伸縮項目的排列順序;在ltr下伸縮項目從右到左排列。
不一樣點:3d

flex-direction:row-reverse:第一個伸縮項目向主軸起點對齊
row-reversecode

box-orient:horizontal;box-direction:reverse:最後一個伸縮項目向主軸終點對齊
row-reverse2blog

flex-direction:column-reverse vs box-orient:vertical;box-direction:reverse

相同點:改變主軸方向和伸縮項目的排列順序;在ltr下伸縮項目從下到上排列。
不一樣點:
flex-direction:column-reverse:第一個伸縮項目向主軸起點對齊。
column-reverseelement

box-orient:vertical;box-direction:reverse:最後一個伸縮項目向主軸終點對齊。
column-reverse2

oreder:integer vs box-ordinal-group:integer

相同點:定義伸縮項目顯示順序。
不一樣點:
oreder:integer:默認值爲0;能夠爲負值。
box-ordinal-group:integer:默認值爲1;取值大於1。

flex-grow:number vs box-flex:number

相同點:定義伸縮項目的擴展因素。
不一樣點:box-flex:number同時定義了伸縮項目的縮小因素。

flex-shrink:number vs box-flex:number

相同點:定義伸縮項目的縮小因素。
不一樣點:box-flex:number同時定義了伸縮項目的擴展因素。

Flexbox分配空間原理

影響Flexbox佈局分配空間的屬性有三個,分別是flex-growflex-shrinkflex-basis

  • flex-grow:當伸縮項目在主軸方向的總寬度 < 伸縮容器,伸縮項目根據擴展因素分配伸縮容器的剩餘空間。

  • flex-shrink:當伸縮項目在主軸方向的總寬度 > 伸縮容器,伸縮項目根據縮小因素分配總寬度超出伸縮容器的空間。

  • flex-basis:伸縮基礎,在進行計算剩餘空間或超出空間前,給伸縮項目從新設置一個寬度,而後再計算。

咱們先來看看如何計算計算拉伸後的伸縮項目寬度,先簡單明瞭的給個公式,再經過栗子來驗證。

伸縮項目擴展寬度 = (項目容器寬度 - 項目寬度或項目設置的flex-basis總和) * 對應的flex-grow比例

拉伸後伸縮項目寬度 = 原伸縮項目寬度 + 擴展寬度

.flexbox-wrap{
    width:550px;
    display: flex;
}
.flexbox-item{
    &:nth-child(1){
        width:60px;
    }
    &:nth-child(2){
        width:70px;
    }
    &:nth-child(3){
        flex-basis:80px;
    }
    &:nth-child(4){
        flex-basis:90px;
    }
    &:nth-child(5){
         flex-basis:100px;
    }
}
@for $i from 1 through 5 {
    .flexbox-item:nth-child(#{$i}){
        flex-grow: $i;
        background-color: rgba(35 * (6-$i), 20 * $i, 35 * $i,1);
    }
}

flex-grow

咱們來計算一下上面栗子中第一個伸縮項目拉伸後的寬度。
對應着公式一步步計算:

// 項目容器寬度
container = 550
// 項目寬度或項目設置的flex-basis總和
itemSum = 60 + 70 + 80 + 90 + 100 = 400
// 第一個伸縮項目對應的flex-grow比例
flexRatio = 1 / ( 1 + 2 + 3 + 4 + 5 ) = 1/15
// 第一個伸縮項目擴展寬度
extendWidth = ( 550 - 400 ) * 1/15 = 10
// 第一個伸縮項目拉伸後的寬度
itemWidth = 60 + 10 = 70

計算後獲得第一個伸縮項目拉伸後的寬度是70px,咱們經過chrome上的盒子模型來看看是否正確
flex-grow-box

chrome計算的結果和咱們計算的結果是一致的。

根據拉伸的計算公式是否是很容易就能推演出壓縮的計算公式呢?

伸縮項目縮小寬度 = (項目寬度或項目設置的flex-basis總和 - 項目容器寬度) * 對應的flex-shrink比例

壓縮後伸縮項目寬度 = 原伸縮項目寬度 - 縮小寬度

繼續用個栗子來驗證公式是否正確

.flexbox-wrap{
    width:250px;
    display: flex;
}
.flexbox-item{
    &:nth-child(1){
        width:60px;
    }
    &:nth-child(2){
        width:70px;
    }
    &:nth-child(3){
        flex-basis:80px;
    }
    &:nth-child(4){
        flex-basis:90px;
    }
    &:nth-child(5){
         flex-basis:100px;
    }
}
@for $i from 1 through 5 {
    .flexbox-item:nth-child(#{$i}){
        flex-shrink: $i;
        background-color: rgba(35 * (6-$i), 20 * $i, 35 * $i,1);
    }
}

flex-shrink

咱們來計算一下上面栗子中第一個伸縮項目壓縮後的寬度。
對應着公式一步步計算:

// 項目容器寬度
container = 250
// 項目寬度或項目設置的flex-basis總和
itemSum = 60 + 70 + 80 + 90 + 100 = 400
// 第一個伸縮項目對應的flex-shrink比例
flexRatio = 1 / ( 1 + 2 + 3 + 4 + 5 ) = 1/15
// 第一個伸縮項目縮小寬度
extendWidth = ( 400 - 250 ) * 1/15 = 10
// 第一個伸縮項目壓縮後的寬度
itemWidth = 60 - 10 = 50

計算後獲得第一個伸縮項目壓縮後的寬度是50px,咱們經過chrome上的盒子模型來看看是否正確
flex-shrink-box

chrome計算的結果和咱們計算的結果不同。
gangga

伸縮項目壓縮的計算方式和拉伸的不同,是由於壓縮會有極端狀況,咱們把第一個伸縮項目的flex-shrink修改成10,此時縮小寬度爲( 400 - 250 ) * ( 10 / 24) = 62.5,縮小的寬度比原寬度要大,計算的壓縮後的寬度變成了負數。

爲了不這種極端狀況,計算縮小比例是要考慮伸縮項目的原寬度。

正確的公式是這樣的

伸縮項目縮小寬度 = (項目寬度或項目設置的flex-basis總和 - 項目容器寬度) (對應的flex-shrink 項目寬度或項目設置的flex-basis比例)

壓縮後伸縮項目寬度 = 原伸縮項目寬度 - 縮小寬度

對應着公式一步步計算:

// 項目容器寬度
container = 250
// 項目寬度或項目設置的flex-basis總和
itemSum = 60 + 70 + 80 + 90 + 100 = 400
// 第一個伸縮項目對應的flex-shrink比例
flexRatio = (1*60) / (1*60+2*70+3*80+4*90+5*100) = 6/130
// 第一個伸縮項目縮小寬度
extendWidth = ( 400 - 250 ) * 6/130 ≈ 6.922
// 第一個伸縮項目壓縮後的寬度
itemWidth = 60 - 6.922 = 53.078

計算後獲得第一個伸縮項目壓縮後的寬度是53.078px,和chrome上的盒子模型是同樣的。

Flexbox屬性縮寫陷阱

上面介紹的flex-growflex-shrinkflex-basis有一個縮寫的寫法flex

flex: flex-grow [flex-shrink] [flex-basis]

flex各類縮寫的值

  • flex: initial == flex: 0 1 auto

  • flex: none == flex: 0 0 auto

  • flex: auto == flex: 1 1 auto

  • flex: number == flex: number 1 0%

在實際項目中,會直接寫使用縮寫的flex來給伸縮項目分配空間,可是使用縮寫屬性會留下一些陷阱,致使表現的結果不盡如人意。

分別使用flexflex-grow來把伸縮項目拉伸填滿容器,看看錶現的差別。

首先看看使用flex-grow拉伸伸縮項目的效果

.flexbox-wrap{
    width:550px;
    display: flex;
}
.flexbox-item{
    flex-grow:1;
    &:nth-child(1){
        width:60px;
    }
    &:nth-child(2){
        width:70px;
    }
    &:nth-child(3){
        width:80px;
    }
    &:nth-child(4){
        width:90px;
    }
    &:nth-child(5){
         width:100px;
    }
}
@for $i from 1 through 5 {
    .flexbox-item:nth-child(#{$i}){
        background-color: rgba(35 * (6-$i), 20 * $i, 35 * $i,1);
    }
}

每一個伸縮項目在原寬度上拉伸相同的寬度
flex-grow1

經過上面的計算拉伸後的伸縮項目寬度,能夠計算第一個伸縮項目拉伸後的寬度

// 項目容器寬度
container = 550
// 項目寬度或項目設置的flex-basis總和
itemSum = 60 + 70 + 80 + 90 + 100 = 400
// 第一個伸縮項目對應的flex-grow比例
flexRatio = 1 / ( 1 + 1 + 1 + 1 + 1 ) = 1/5
// 第一個伸縮項目擴展寬度
extendWidth = ( 550 - 400 ) * 1/5 = 30
// 第一個伸縮項目拉伸後的寬度
itemWidth = 60 + 30 = 90

flex-grow1-box

而後咱們把flex-grow:1替換成flex:1,下面是表現的效果,伸縮項目拉伸後的寬度變成同樣了。
flex1

從chrome的盒子模型可看到伸縮項目拉伸後寬度變成了110px,伸縮容器等分了容器的寬度。
flex1-box

flex:1展開後是flex:1 1 0%flex-grow:1至關於flex:1 1 auto,二者的區別在於flex-basis的值不一樣。flex:1爲項目寬度從新設置了寬度爲0,因此可分配空間爲整個容器,從公式計算上能夠更直觀理解:

// 項目容器寬度
container = 550
// 項目寬度或項目設置的flex-basis總和
itemSum = 0 + 0 + 0 + 0 + 0 = 0
// 第一個伸縮項目對應的flex-grow比例
flexRatio = 1 / ( 1 + 1 + 1 + 1 + 1 ) = 1/5
// 第一個伸縮項目擴展寬度
extendWidth = ( 550 - 0 ) * 1/5 = 110
// 第一個伸縮項目拉伸後的寬度
itemWidth = 0 + 110 = 110

須要注意的Flexbox特性

無效屬性

  • column-*在伸縮容器無效

  • float和clear在伸縮項目無效

  • vertical-align在伸縮項目無效

  • ::first-line and ::first-letter在伸縮容器無效

伸縮容器中的非空字符文本節點也是伸縮項目

<div class="flexbox-wrap">
    <span class="flexbox-item">1</span>
    <span class="flexbox-item">2</span>
    我是個假文本
    <span class="flexbox-item">3</span>
    <span class="flexbox-item">4</span>
    <span class="flexbox-item">5</span>
</div>

textelement

margin摺疊

  • 伸縮容器和伸縮項目的margin不會摺疊

  • 伸縮項目間的margin不會摺疊

舊版Flexbox的BUG

伸縮項目爲行內元素要加display:block;或display:flex

歡迎關注:Leechikit
原文連接:segmentfault.com

到此本文結束,歡迎提問和指正。寫原創文章不易,若本文對你有幫助,請點贊、推薦和關注做者支持。

相關文章
相關標籤/搜索