CSS你必须知道的知识 --- flex

339 阅读9分钟

什么是flex

flex,指的display:flex,元素开启弹性盒模型,是一种一维的布局模型。它给 flexbox 的子元素之间提供了强大的空间分布和对齐能力

一维的布局:一次只能处理一个维度上的元素布局,一行或者一列

二维布局:grid(敬请期待下一篇),可以同时处理行和列上的布局。

为什么要用flex

回想下我们再做两栏或者三栏布局时,没有flex时候用的最多的是什么,float和position对吧,既然说到这两种,我们先看下再进行三栏布局的时候,这两种该怎么用

假设高度已知,请写出三栏布局,其中左栏、右栏宽度各为 300px,中间自适应

公共样式

<style>
    .wrapper .inner{
        height:100px;
        text-align: center;
        line-height: 100px;
    }
    .wrapper .inner.left{
        background:red;
        width: 300px;
    }
    .wrapper .inner.center{
        background:green;
    }
    .wrapper .inner.right{
        background:blue;
        width: 300px;
    }
</style>
  • float布局
</style>       
    .float .left{
        float:left;	
    }
    .float .right{
        float:right;
    }
</style>
<div class="wrapper float">
    <div class="inner left">左</div>
    <div class="inner right">右</div>
    <div class="inner center">中</div>
</div>
  • position布局
<style>
    .wrapper.position{
        position: relative;
        height: 100px;
    }
    .position .left{
        position: absolute;
        left:0px;
    }
    .position .center{
        position: absolute;
        left:300px;
        right: 300px;
    }
    .position .right{
        position: absolute;
        right:0px;
    }
</style>
<div class="wrapper position">
    <div class="inner left">左</div>
    <div class="inner center">中</div>
    <div class="inner right">右</div>
</div>

float布局用起来很简单,但是会因为浮动元素是脱离文档流,要做清除浮动,这个处理不好的话,会带来很多问题,比如高度塌陷等。(例子中因为有定高,所有就没有清理浮动)

position光看起来就很麻烦,每个子元素都要设置定位,更何况绝对定位也是脱离文档流的,一个处理不好,造成高度坍塌,下面的元素就直接上来了

为了解决这些痛点,flex就扛起了这块大旗,下面我们看看flex是怎么用的

<style>
        .wrapper.flex{
            display: flex;
        }
        .flex .center{
            flex-grow: 1;
        }
</style>
<div class="wrapper flex">
    <div class="inner left">左</div>
    <div class="inner center">中</div>
    <div class="inner right">右</div>
</div>

两行样式代码,元素也没脱离文档流,是不是很简单,but...,用之前请注意各浏览器兼容性,剩下的就是你得掌握使用它了

css兼容性查询

怎么用flex

上文说过flex是处理一维布局的,说到维度,我的第一反映就是轴(x/y/z轴),这边我们换个说法主轴和交叉轴(主轴定了,交叉轴也就定了,交叉轴垂直于主轴,这个概念很重要哦,justify-content和align-items这两货的对齐方向可不是我们以为的水平和垂直哦),有了这个概念接下来我们就先说下flex的第一个属性

父元素(弹性盒子本身)

flex-direction

通过定义flex容器的主轴方向来决定felx子项在flex容器中的位置。这将决定flex需要如何进行排列

flex-direction如果你选择了 row 或者 row-reverse,你的主轴将沿着 inline 方向延伸。

flex-direction如果你选择 column 或者 column-reverse 时,你的主轴会沿着上下方向延伸 — 也就是 block 排列的方向。

flex-direction:row | row-reverse | column | column-reverse

默认值:row
适用于:flex容器
继承性:无
动画性:否
计算值:指定值

取值:
row: 主轴与行内轴方向作为默认的书写模式。即横向从左到右排列(左对齐)。
row-reverse: 对齐方式与row相反。
column: 主轴与块轴方向作为默认的书写模式。即纵向从上往下排列(顶对齐)。
column-reverse: 对齐方式与column相反。 

公共样式

<style>
    .wrapper{
        width: 300px;
        height: 300px;
        display:flex;
        border: 1px solid #000;
    }
    .box{
        width: 100px;
        height: 100px;
        line-height: 100px;
        text-align: center;
        font-size: 30px;
    }
    .red{
        background-color: red;
    }
    .green{
        background-color: green;
    }
    .blue{
        background-color: blue;
    }
</style>
<div class="wrapper flex-direction">
    <div class="box red">左</div>
    <div class="box green">中</div>
    <div class="box blue">右</div>
</div>

搞清楚排列方向后,你以为就结束了吗,我只能说,too young too sample

正所谓鲲之大,一行装不下,碰到这种情况该怎么办呢

flex-wrap

控制flex容器是单行或者多行,同时横轴的方向决定了新行堆叠的方向

flex-wrap:nowrap | wrap | wrap-reverse

默认值:nowrap
适用于:flex容器
继承性:无
动画性:否
计算值:指定值

取值:
nowrap: flex容器为单行。该情况下flex子项可能会溢出容器 
wrap: flex容器为多行。该情况下flex子项溢出的部分会被放置到新行,子项内部会发生断行 
wrap-reverse: 反转 wrap 排列。 

公共样式

<style>
    .wrapper {
        width: 310px;
        height: 310px;
        display: flex;
        border: 1px solid #000;
    }

    .box {
        width: 100px;
        height: 100px;
        line-height: 100px;
        text-align: center;
        font-size: 30px;
        box-sizing: border-box;
        margin: 1px;
    }
    .cornflowerblue {
        background-color: cornflowerblue;
    }
</style>
<div class="wrapper flex-wrap">
    <div class="box cornflowerblue">1</div>
    <div class="box cornflowerblue">2</div>
    <div class="box cornflowerblue">3</div>
    <div class="box cornflowerblue">4</div>
    <div class="box cornflowerblue">5</div>
</div>

各位小伙伴看完上面的例子你是不是会有这样的疑问,为什么wrap和wrap-reverse 这两货 4/5box不是挨着123呢,别急,再次之前针对主轴上的内容我还要加那么一丢丢的知识

flex-flow

复合属性,flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap

好了我们接上文接着聊

justify-content

定义弹性盒子元素在主轴(横轴)方向上的对齐方式

justify-contentflex-start | flex-end | center | space-between | space-around

默认值:flex-start
适用于:flex容器
继承性:无
动画性:是
计算值:指定值

flex-start: 弹性盒子元素将向行起始位置对齐。该行的第一个子元素的主起始位置的边界将与该行的主起始位置的边界对齐,同时所有后续的伸缩盒项目与其前一个项目对齐。 
flex-end: 弹性盒子元素将向行结束位置对齐。该行的第一个子元素的主结束位置的边界将与该行的主结束位置的边界对齐,同时所有后续的伸缩盒项目与其前一个项目对齐。 
center: 弹性盒子元素将向行中间位置对齐。该行的子元素将相互对齐并在行中居中对齐,同时第一个元素与行的主起始位置的边距等同与最后一个元素与行的主结束位置的边距(如果剩余空间是负数,则保持两端相等长度的溢出)。 
space-between: 弹性盒子元素会平均地分布在行里。如果最左边的剩余空间是负数,或该行只有一个子元素,则该值等效于'flex-start'。在其它情况下,第一个元素的边界与行的主起始位置的边界对齐,同时最后一个元素的边界与行的主结束位置的边距对齐,而剩余的伸缩盒项目则平均分布,并确保两两之间的空白空间相等。 
space-around: 弹性盒子元素会平均地分布在行里,两端保留子元素与子元素之间间距大小的一半。如果最左边的剩余空间是负数,或该行只有一个伸缩盒项目,则该值等效于'center'。在其它情况下,伸缩盒项目则平均分布,并确保两两之间的空白空间相等,同时第一个元素前的空间以及最后一个元素后的空间为其他空白空间的一半。

公共样式

<style>
    .wrapper {
        width: 310px;
        height: 62px;
        display: flex;
        border: 1px solid #000;
    }
    .box {
        width: 100px;
        height: 100px;
        line-height: 100px;
        text-align: center;
        font-size: 30px;
        box-sizing: border-box;
        margin: 1px;
    }
    .middle {
        width: 60px;
        height: 60px;
        line-height: 60px;
    }
    .cornflowerblue {
        background-color: cornflowerblue;
    }
</style>
<div class="wrapper flex-direction justify-content">
    <div class="box cornflowerblue middle">1</div>
    <div class="box cornflowerblue middle">2</div>
    <div class="box cornflowerblue middle">3</div>
</div>

align-items

align-items属性定义项目在交叉轴上如何对齐。

align-itemsflex-start | flex-end | center | baseline | stretch

默认值:stretch
适用于:flex容器
继承性:无
动画性:是
计算值:指定值

flex-start:交叉轴的起点对齐。
flex-end:交叉轴的终点对齐。
center:交叉轴的中点对齐。
baseline: 项目的第一行文字的基线对齐。
stretch(默认值):如果项目交叉轴上未设置大小或设为auto,元素将撑满交叉轴。

公共样式

<style>
    .wrapper {
        width: 310px;
        height: 310px;
        display: flex;
        border: 1px solid #000;
    }
    .box {
        width: 100px;
        height: 100px;
        line-height: 100px;
        text-align: center;
        font-size: 30px;
        box-sizing: border-box;
        margin: 1px;
    }
    .small {
        width: 30px;
        height: 30px;
        line-height: 30px;
    }
    .middle {
        width: 60px;
        height: 60px;
        line-height: 60px;
    }
    .large {
        width: 90px;
        height: 90px;
        line-height: 90px;
    }
    .only-width{
        width:20px;
        text-align: center;
        margin: 1px;
    }
    .cornflowerblue {
        background-color: cornflowerblue;
    }
</style>
<div class="wrapper flex-direction align-items">
    <div class="box cornflowerblue small">1</div>
    <div class="box cornflowerblue middle">2</div>
    <div class="box cornflowerblue large">3</div>
    <div class="cornflowerblue only-width">4</div>
</div>

本例中当flex-direction:column时,其实已经是定义了多根交叉轴,多根交叉轴的对齐方式则又有新的样式

algin-content

align-content定义了多根交叉轴的对齐方式。如果项目只有一根轴线,该属性不起作用。

align-contentflex-start | flex-end | center | space-between | space-around | stretch

默认值:stretch
适用于:多行的弹性盒模型容器
继承性:无
动画性:是
计算值:指定值

flex-start:与交叉轴的起点对齐。
flex-end:与交叉轴的终点对齐。
center:与交叉轴的中点对齐。
space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
stretch(默认值):轴线占满整个交叉轴。

样式

<style>
        .wrapper {
            width: 350px;
            height: 350px;
            display: flex;
            border: 1px solid #000;
        }
        .box {
            width: 100px;
            height: 100px;
            text-align: center;
            font-size: 1px;
            margin: 1px;
            background-color: cornflowerblue;
        }
        .small {
            width: 50px;
            height: 50px;
        }
        .middle {
            width: 100px;
            height: 50px;
        }
        .large {
            width: 150px;
            height: 50px;
        }
        .no-width {
            text-align: center;
            font-size: 1px;
            margin: 1px;
            background-color: cornflowerblue;
        }
        .flex-wrap {
            flex-wrap: wrap;
        }
</style>
    <div class="wrapper flex-wrap align-content">
        <div class="box small">宽50px</div>
        <div class="box middle">宽100px</div>
        <div class="no-width">没有宽高</div>
        <div class="box large">宽150px</div>

        <div class="box small">宽50px</div>
        <div class="box middle">宽100px</div>
        <div class="no-width">没有宽高</div>
        <div class="box large">宽150px</div>
    </div>

看到这里flex-wrap的问题大家是否已经明白其中的原因了吧

子元素(弹性盒子内children元素)

order

order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。

<style>
.wrapper {
    width: 350px;
    height: 350px;
    display: flex;
    border: 1px solid #000;
}
.box {
    width: 100px;
    height: 100px;
    /* line-height: 100px; */
    text-align: center;
    font-size: 1px;
    margin: 1px;
    background-color: cornflowerblue;
}
.order0{
    order: 0;
}
.order1{
    order: 1;
}
.order2{
    order: 2;
}
.order3{
    order: 3;
}
.order4{
    order: 4;
}
</style>

<div class="wrapper">
    <div class="box order1">order1</div>
    <div class="box order0">order0</div>
    <div class="box order3">order3</div>
    <div class="box order4">order4</div>
    <div class="box order2">order2</div>
</div> 

flex-grow

flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。

flex-grow:<number>

默认值:0
适用于:flex子项
继承性:无
动画性:是
计算值:指定值

取值:
<number>: 用数值来定义扩展比率。不允许负值 

样式

<style>
.wrapper {
    width: 606px;
    height: 103px;
    display: flex;
    border: 1px solid #000;
}
.box {
    width: 100px;
    height: 100px;
    text-align: center;
    font-size: 1px;
    margin: 1px;
    background-color: cornflowerblue;
}
.grow1{
    flex-grow: 1;
}
.grow2{
    flex-grow: 2;
}
</style>

<div class="wrapper">
    <div class="box grow1">grow1宽100px</div>
    <div class="box">宽100px</div>
    <div class="box grow2">grow2宽100px</div>
</div> 

计算方式如下:

  • 剩余空间:x

  • 假设有三个flex item元素,flex-grow 的值分别为a, b, c

  • 每个元素可以分配的剩余空间为: a/(a+b+c) * x,b/(a+b+c) * x,c/(a+b+c) * x

剩余空间宽度:606 - 2*3(子元素margin) - 100 = 300px

grow1的宽度:100 + 300*1/(1+2) = 200px

grow2的宽度:100 + 300*2/(1+2) = 300px

flex-shrink

flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。

flex-shrink:<number>

默认值:1
适用于:flex子项
继承性:无
动画性:是
计算值:指定值

取值:
<number>:用数值来定义收缩比率。不允许负值 

样式

<style>
.wrapper {
    width: 200px;
    height: 103px;
    display: flex;
    border: 1px solid #000;
}
.box {
    width: 100px;
    height: 100px;
    text-align: center;
    font-size: 1px;
    margin: 1px;
    background-color: cornflowerblue;
}
.shrink1{
    flex-shrink: 1;
}
.shrink2{
    flex-shrink: 2;
}
</style>

<div class="wrapper">
    <div class="box shrink1">shrink1宽100px</div>
    <div class="box">宽100px</div>
    <div class="box shrink2">shrink2宽100px</div>
</div> 

计算方式:

  • 三个flex item元素的width: w1, w2, w3
  • 三个flex item元素的flex-shrink:a, b, c
  • 计算总压缩权重: sum = a * w1 + b * w2 + c * w3
  • 溢出空间:所有子元素width + margin +border - 父元素width
  • 计算每个元素压缩率: S1 = a * w1 / sum,S2 =b * w2 / sum,S3 =c * w3 / sum
  • 计算每个元素宽度:width - 压缩率 * 溢出空间

压缩权重:1*100 + 1*100 +2*100 +2*3(margin) = 406

溢出空间:406 - (100+100+100) = 106

shrink1的宽度:100 - 106 * 1*100/400 = 73.5

无shrink的宽度:100 - 106 * 1*100/400 = 73.5

shrink2的宽度:100 - 106 * 2*100/400 = 47

flex

flex属性是flex-grow, flex-shrinkflex-basis的简写,默认值为0 1 auto
flexnone | <' flex-grow '> <' flex-shrink >'? || <' flex-basis '>

默认值:0 1 auto
适用于:flex子项
继承性:无
动画性:否
计算值:看各分拆属性

align-self

align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch

align-self:auto | flex-start | flex-end | center | baseline | stretch

默认值:auto
适用于:flex子项
继承性:无
动画性:是
计算值:如果值为「auto」,则计算值为父元素的 <' align-items '> 值,否则为指定值。

样式

<style>
.wrapper {
    width: 603px;
    height: 303px;
    display: flex;
    border: 1px solid #000;
}

.box {
    width: 100px;
    text-align: center;
    font-size: 1px;
    margin: 1px;
    background-color: cornflowerblue;
}
.align-self-auto {
    align-self: auto;
}

.align-self-flex-start {
    align-self: flex-start;
}

.align-self-flex-end {
    align-self: flex-end;
}

.align-self-center {
    align-self: center;
}

.align-self-baseline {
    align-self: baseline;
}

.align-self-stretch {
    align-self: stretch;
}
</style>
<div class="wrapper">
    <div class="box align-self-auto">auto</div>
    <div class="box align-self-flex-start">flex-start</div>
    <div class="box align-self-flex-end">flex-end</div>
    <div class="box align-self-center">center</div>
    <div class="box align-self-baseline">baseline</div>
    <div class="box align-self-stretch">stretch</div>
</div>