css flex布局

303 阅读18分钟
原文链接: binghuixie.cn

前言

flex的出现使css的布局大大简化,例如两栏自适应等常见CSS布局通过使用flex布局能非常方便的达到,然而在网上常见的flex布局的教程中,许多属性的深层意思都没有提及,例如flex-growflex-shrink属性,正好这段时间我也开始在学flex布局,而且我这个人一向的习惯就是新学的东西一定要有笔记,以前用那些办公软件记笔记,结果有一次都丢了,自那以后搭建起了博客,一些笔记就都放在了博客上面,所以今天传上来这个flex布局的笔记,希望大家看过后能有所收货。
ps:以下所有内容是我在看完菜鸟教程以及阮一峰老师的博客后所写,链接给大家。

目录

1.什么是flex布局
2.flex布局中一些相关的概念
3.指定flex布局
4.容器属性:flex-direction
5.容器属性:flex-wrap
6.容器属性:flex-flow
7.容器属性:justify-content
8. 容器属性:align-items
9.容器属性:align-content
10.项目属性:order
11.项目属性:flex-grow
12.项目属性:flex-shrink
13.项目属性:flex-basis
14. 项目属性:flex
15.项目属性:align-self
内容比较多,我自己一次也写不完,慢慢来吧~

1.什么是flex布局

我们都知道,CSS的布局是一个难点,先不说flex,常见的CSS布局比如说有两栏自适应,两翼齐飞布局什么的,其实说白了只要涉及到元素的位置的安排,都算是布局,那在flex之前我们常用的手段有float,position,table等,但是这些属性本身不是为布局而生的,就比如说float,它本质就是实现一个文字环绕效果,但是用这些属性吧,你要是理解的不够深入,还会出现各种各样的坑,比如说一会脱离文档流啦(float,position的absolute和fixed),position的定位原点什么的,会导致网页布局乱七八糟。
那么flex布局,flex是flexible Box的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。目前,flex布局已经得到所有浏览器的支持,这意味着,现在可以放心的使用这项技术

2.flex布局中一些相关的概念

这节介绍一些flex布局中的一些定义,一些概念性的东西

我们把设置为flex布局的容器叫做flex容器,简称“容器”,该容器的所有的子元素会自动成为该容器的成员,称为flex项目,简称“项目”。
容器默认存在两根轴,一个是水平的主轴(main axis)(如图),还有一个是垂直的交叉轴(cross axis)。
主轴开始的位置叫做main start,结束位置叫做main end;交叉轴开始的位置叫做cross start,结束的位置叫做cross end。
容器的子元素也就是项目默认沿着主轴排列,单个项目占据的主轴空间叫做main size,占据的交叉空间叫做cross size。

3.指定flex布局

上面说过,设置了flex布局的容器叫做flex容器,我们可以通过display属性将该容器声明为一个flex布局的容器:

.container{
    display:flex;
}

行内元素也可以设置flex布局

.container{
    display:inline-flex;
}

注意一个问题,容器设置为flex布局以后,其子元素的float,clear以及vertical-align都会失效

4.容器属性:flex-direction

注:从下面开始,凡是标注了“容器属性”后面的所有属性都是设置在容器也就是父元素身上的。标注了“项目属性”的所有属性都是设置在子元素上面的
flex-direction决定主轴(水平)的方向(即项目(子元素)的排列方向)
flex-direction有四种取值
(1)row(默认值):主轴在水平方向,起点在左端
(2)row-reverse:主轴在水平方向,起点在右端
(3)column:主轴在垂直方向,起点在上端
(4)column-reverse:主轴在垂直方向,起点在下端

从例子中能更好的学习理解,所以我做了下面的demo
效果如下:




5.容器属性:flex-wrap

默认情况下,项目都排在一条线(又称”轴线”)上。flex-wrap属性定义,如果一条轴线排不下,如何换行。
分情况讨论,当flex-direction的值为row时,如果不设置flex-wrap属性,即取默认值的时候,所有项目会默认从左到右排列在一行,如果所有项目的宽度加起来超过父容器的宽度,则会压缩项目的宽度。如图所示

在代码中我默认设置了每一个项目的宽度都是150px,然而可以发现,12个150px加起来12×150=1800px已经超过了父容器的宽度,然而此时并没有换行,而是压缩了子元素的宽度,使其不足150px

设置flex-wrap:wrap时,在父容器放不下所有项目时,会换到下一行排列,多余的不足一个项目宽度的地方会空出来,不会填满

设置flex-wrap:wrap-reverse的时候,会从最下面开始排列,排列不下了在往上一行放,以此类推

当flex-direction的值设置为column时,如果flex-wrap取默认值也就是no-wrap,则会按顺序以一列的形式从上到下排列,如果flex-wrap设置了wrap,则会现在一列里面排列,如果高度超出父容器的高度,则会换到第二列,以此类推,直到排列完(不会被父容器宽度所限制)
例如:

flex-direcrtion:column,flex-wrap:wrap

flex-direcrtion:column,flex-wrap:wrap-reverse


其余flex-direction的值与此相似

6.容器属性:flex-flow

flex-flow是flex-direction属性和flex-wrap属性的简写,格式如下:
flex-flow: ||
具体内容见上面,这里不详细介绍

7.容器属性:justify-content

justify-content属性定义了项目在主轴上的对齐方式。
justify-content有六种取值
(1)justify-content:flex-start(左对齐)

(2)justify-content:flex-end(右对齐)

(3)justify-content:center(水平居中)

(4)justify-content:space-around(每个项目两侧的间隔相等,这样项目之间的间隔比项目与边框的间隔大一倍)

(5)justify-content:space-between(两端对齐)

(6)justify-content:space-evenly(项目与项目,项目与边界的间隔都相等 )

注:以上所有排列都是在父容器设置了flex-direction:row的前提下,其余的自己可以动手试一下

8.容器属性:align-items

align-items属性定义项目在交叉轴(竖直方向)上如何对齐。
align-items有5种取值
(1)align-items:flex-start
align-items:flex-start定义项目从交叉轴的起点开始对齐

(2)align-items:flex-end
align-items:flex-end定义项目从交叉轴的末端开始对齐

(3)align-items:center
align-items:center定义从交叉线的中点开始对齐(垂直居中)

(4)align-items:baseline
align-items:baseline定义从第一行文字的基线开始对齐

(5)align-items:stretch
align-items:stretch定义:如果项目未设置高度或设为auto,将占满整个容器的高度

9.容器属性:align-content

align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。
那怎么样才能有多个轴线呢?其实,只需要让项目换行显示就可以,所以我们需要设置flex-direction属性的值为wrap或者wrap-reverse(例子中使用的是wrap),也就是当所有项目一行放不下的时候,项目会换行显示,这样子就会有多个轴线了。
该属性有7个取值
(1)align-content:flex-start
align-content:flex-start定义项目与交叉轴的起点对齐

(2)align-content:flex-end
align-content:flex-end定义项目与交叉轴的终点对齐

(3)align-content:center
align-content:center定义项目与交叉轴的中点对齐

(4)align-content:space-between
align-content:space-between定义项目与交叉轴两端对齐,轴线之间的间隔平均分布。

(5)align-content:space-around
align-content:space-around定义项目之间每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。

(6)align-content:space-evenly
align-content:space-evenly定义所有间距都相等

(7)align-content:stretch
align-content:stretch定义轴线占满整个交叉轴,项目不设置高度或者高度为auto

10.项目属性:order

注:以下所有属性都是设置在子元素上面的
order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。

11.项目属性:flex-grow

flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大
关于这个属性以及下面的flex-shrink属性,在菜鸟教程和阮一峰老师的博客中并没有详细的对此进行解释,只是有上面的一句话,所以我把我自己的理解以及计算方式整理在下面,以供参考
首先,flex-grow属性决定了在所有项目的排列方向上还有剩余的空间时,如何分配这些剩余的空间,其值为一个权重,默认是0,也就是不扩张,父元素剩余的空间会按照这个权重来进行分配。
先来看一个demo

父容器的宽度设置为1000px,5个项目(子元素)的宽高均为40px。现在是我们没有设置flex-grow熟悉的时候,也就是该属性的值默认为0的时候,此时所有项目默认不扩张,按照自身的宽高进行排列,可以看出,父元素上还有如下几个属性,使项目能垂直居中排列

    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;

我们现在给标号为1的项目加上属性flex-grow:1,先看看是什么效果

可以看出,标号为1的项目占满了剩下的所有宽度,把其余几个项目都挤到了右边
再来看几个例子
给标号为1的项目设置flex-grow:1,给标号为2的项目设置flex-grow:2

可以看出,2项目的宽度接近1项目宽度的两倍,父元素排列方向上的宽度被占满
给5个项目的flex-grow属性分别设置为1到5

    .items1{
        flex-grow: 1;
    }
    .items2{
        flex-grow: 2;
    }
    .items3{
        flex-grow: 3;
    }
    .items4{
        flex-grow: 4;
    }
    .items5{
        flex-grow: 5;
    }


可以看出,5个项目的宽度相对于第一个项目的宽度依次扩大了其序号所对应的倍数
上面两个例子对应的所有项目的flex-grow属性之和都大于1,我们来看一个不大于1的例子
所有项目的flex-grow属性对应的值之和小于1

    .items1{
        flex-grow: 0.1;
    }
    .items2{
        flex-grow: .1;
    }
    .items3{
        flex-grow: .05;
    }
    .items4{
        flex-grow: .2;
    }
    .items5{
        flex-grow: .15;
    }


可以看出,扩大后他们并没有占满父元素所有的剩余空间

总结

从上面的例子中,我们可以看出,flex-grow属性之和大于1和小于1显示的效果是不一样的,那么我们分开来讨论一下他的计算方式
Ⅰ.flex-grow属性之和大于1时,五个项目的宽度都是40px,那么剩余空间为1000-5*40 = 800px。
五个项目的flex-grow属性为1,2,3,4,5,合起来是sum = 15,所以5个项目分配到的多余的空间为

  • 800 * 1 / 15 = 53.3333333px
  • 800 * 2 / 15 = 106.66666667px
  • 800 * 3 / 15 = 160px
  • 800 * 4 / 15 = 213.333333px
  • 800 * 5 / 15 = 266.666667px

那么这5个项目最终扩张以后的宽度为93.33333333px,146.6666667px,200px,253.333333px,306.6666667px(在40px的基础上加上扩展的宽度)
看一下项目的宽度





和我们计算的所差无几,说明这样算是正确的
我们还可以发现一点,flex-grow属性值为2的项目最终的宽度并不是flex-grow属性值为1的项目的最终宽度的2倍,而是扩展的(变化的)宽度为2倍!!!!!!
Ⅱ.flex-grow属性之和小于1时,五个项目的宽度都是40px,同样剩余空间为1000-5*40 = 800px。
五个项目的flex-grow属性值为0.1,0.05,0.2,0.15,0.3,合起来是sum = 0.8 < 1。同样,剩余800px的空间
当所有元素的flex-grow属性之和小于1的时候,上面式子中的sum将会使用 1 来参与计算,而不论它们的和是多少,也就是说,这个 0.8 没有什么作用,此时的sum默认按照 1 来计算。
那么五个项目扩展的宽度为

  • 800 * 0.1 / 1 = 80px
  • 800 * 0.05 / 1 = 40px
  • 800 * 0.2 / 1 = 160px
  • 800 * 0.15 / 1 = 120px
  • 800 * 0.3 / 1 = 240px

所以最终 5 个项目的宽度分别为 120px,80px,200px,160px,280px
可以发现,800px - 80px - 40px - 160px - 120px - 240px = 160px,也就是说,还剩下160px的宽度没有被占满
同样我们可以在浏览器里面查看各个项目的宽度





与我们计算的一毛一样,完美~

12.项目属性:flex-shrink

上面的flex-grow属性会在父元素有多余的空间的使用通过子元素的扩张来占满这些剩余空间,那么同样就会有在父容器空间不够时将子元素收缩以来适应父元素的空间的属性了,也就是flex-shrink属性
flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
那么就像上面flex-grow属性一样,flex-shrink属性也有自己的计算方式,以此来计算子元素该如何收缩。
同样,我们先来看flex-grow属性值适合大于1的情况
flex-grow属性值之和大于1
我们设父容器的宽度为1000px,5个项目的宽度均为300px,这样多出来的宽度就是300 5 - 1000 = 500px,5个项目flex-shrink属性的值依次为1,2,3,4,5。
那么这多出的500px将会由5个项目分别收缩一定的比例来消化。
项目收缩的计算公式:超出的总宽度
收缩因子 / 收缩权重 项目宽度
收缩因子即为flex-shrink属性的值
收缩权重的计算公式:项目宽度
对应的收缩因子
所以本例中的权重为:300 1 + 300 2 + 300 3 + 300 4 + 300 * 5 = 4500px
所以我们计算每个项目的收缩量如下:

  • 500 1 / 4500 300 = 33.3333333px
  • 500 2 / 4500 300 = 66.6666667px
  • 500 3 / 4500 300 = 100px
  • 500 4 / 4500 300 = 133.333333px
  • 500 5 / 4500 300 = 166.666667px

收缩后每个项目的宽度为 266.66667px,233.33333px,200px,166.666667px,133.333333px
我们看一下浏览器显示的宽度以验证我们的结果





flex-grow属性值之和小于1
flex-grow属性值之和小于1时,并不会完美的收缩到正好占满所有的空间而没有溢出,就像flex-grow属性的所有值之和小于1一样,也会按照和sum为1来进行收缩
现在设flex-grow属性值分别为0.05,0.1,0.15,0.2,0.25,
权重为:300 (0.05 + 0.1 + 0.15 + 0.2 + 0.25) = 225px
那么 5 个项目收缩的总和是(0.05 + 0.1 + 0.15 + 0.2 + 0.25)
500px 也就是375px,也就是说,最终收缩完,项目综合还是会超出父容器125px的宽度
每个项目收缩的空间为

  • 300 0.05 375 / 225 = 25px
  • 300 0.1 375 / 225 = 50px
  • 300 0.15 375 / 225 = 75px
  • 300 0.2 375 / 225 = 100px
  • 300 0.25 375 / 225 = 125px

所以收缩后的每个项目的宽度为 275px,250px,225px,200px,175px
收缩后的宽度之和为:275 + 250 + 225 + 200 + 175 = 1125px,正好多出125px。
浏览器里面显示如下:





可以看出,最终所有项目的和的宽度还是超过父元素的宽度的

13.项目属性:flex-basis

flex-basis属性用于设置或检索弹性盒伸缩基准值。
什么意思,就是说flex-basis属性用于设置项目的基准宽度(width),与width属性有异曲同工之妙,但是与width属性又有很大不同。
我们都知道,如果没有给一个容器设置宽度的话,那么容器的宽度就是内容(content)的宽度,而flex-basis属性设置后的宽度会覆盖width属性所设置的宽度,也就是说,三者的优先级是这样的

content < width < flex-basis

总的来说就是

  • 如果没有设置flex-basis属性,那么flex-basis的大小就是项目的width属性大小
  • 如果没有设置width属性,那么flex-basis的大小就是项目的内容(content)宽度大小

下面我们来看一些生动的例子
我们将容器的宽度设置为1000px,为了方便查看我们令其局中显示(margin: 0 auto;)

    .wrapper{
        width: 1000px;
        border: 1px solid #000;
        box-sizing: border-box;
        margin: 0 auto;
        background-color: #CCCCCC;
        height: 400px;
        display: flex;
        flex-direction: row;
        flex-wrap: nowrap;
    }

给容器下面的每一个项目都设置宽度(width)为100px,flex-basis的值为150px,然后查看浏览器

宽度为150px,①说明flex-basis属性会覆盖width属性的取值
flex-basis属性也会被max-width属性与min-width属性所限制,我们看例子

    .items{
        width: 100px;
        flex-basis: 150px;
        /*flex-grow: 1;*/
        min-width: 250px;
        height: 250px;
        background-color: #00A000;
        border: 1px solid #000000;
        box-sizing: border-box;
    }

在上面的代码中,设置了项目的flex-basis属性值为150px,然而设置了min-width属性的值为250px,也就是说,如果min-width属性占了上风,四个项目会正好填满父容器,那是不是这样子呢?

很明显,这次是min-width属性占了上风,那么同样,我们在设置了max-width属性后,同样会限制flex-basis属性,如果flex-basis属性的值超过了设定的max-width属性的之后,那么最终显示的会使max-width属性所设定的值。
那么当我们设置的所有项目的flex-basis属性值之和大于父容器的宽度后,那么此时起作用的将会是flex-shrink属性了,同样,当项目flex-basis属性值之和达不到父容器的宽度时,我们也可以使用flex-grow属性使其扩展到占满父元素,有关这两个属性已经在上面有详解,这里就不再细说了。

14.项目属性:flex

flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。
该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。
flex属性因为是三种属性的简写,而flex-growflex-shrink是起完全相反的作用的两个属性,所以当然要分情况讨论当这两个属性在不同的项目与容器宽度之比下起到的作用,举个栗子
如果父容器的宽度小于项目宽度(此时的宽度使用到的flex-basis属性设置的)之和,那么flex-shrink属性就会起作用,具体计算方式见上
如果父容器的宽度大于项目宽度(此时的宽度使用到的flex-basis属性设置的)之和,那么flex-grow属性就会起作用,具体计算方式见上

15.项目属性:align-self

align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch
align-self属性的取值有autoflex-startflex-endcenterbaselinestretch,每一个值的意思就是上面所说到的意思
我们可以通过看一个例子演示来形象的理解该属性的效果

其中,在父容器中设置了justify-content:centeralign-items:center使所有项目居中显示,然后给第二个项目设置了align-self:flex-start使其从顶端开始排列,给第三个项目设置了align-self:flex-end使其从底端开始排列,同时覆盖了原来的align-items的效果。