一文彻懂flex布局

1,977 阅读6分钟

接触flex布局也很长时间了,大多用它来处理一些水平、垂直对齐的场景,今天看官方规范文档,才发现flex还有很多有用且强大的特性,看完觉得收益匪浅,有必要总结分享出来。

这是我参与更文挑战的第6天,活动详情查看: 更文挑战

定义

我们都知道块布局、行内布局、表格布局、定位布局是常用的css布局,它们能满足web项目中的大部分业务场景。

然而随着web应用从页面app的过渡,那些用来处理文档、页面式的布局方式在应对web App时显的捉襟见肘。

因此w3c 引入flex布局,它非常擅长处理容器内元素的排列和对齐,并且支持指定排列方向。

所以flex布局是一种新的布局模型,用来处理复杂的web应用或者页面的布局。

关于flex的几个概念

flex容器类型

我们通过给一个元素设置display:flex|inline-flex来将该元素声明为一个flex容器,该flex容器内的子元素称为flex item

声明一个flex容器会生成一个新的flex布局上下文,在这个布局上下文里:

  • 子节点的浮动效果会被忽略
  • 绝对定位元素不会参与flex布局(flex布局不会影响绝对定位子元素,因为它已经独立于文档流了)。

flex容器内的子元素称为flex item

flex表示块级容器、而inline-flex表示行内容器,两者的区别是:

  • 第一个独占一行
  • 另一个表现类似于行内块。

主轴和交叉轴(次轴)

我们可以通过flex-direction指定一个flex容器的流的方向,其flex item将遵从这个方向,这个方向上的轴我们称为主轴(main axis),相对主轴的次轴我们称之为交叉轴(cross axis),主轴的尺寸称为main size,交叉轴的尺寸称为cross size

image.png

另:主轴可以水平轴也可以是垂直轴,同样交叉轴亦然。主轴和交叉轴永远是交叉关系

先说"弹性"

我们知道flex布局的中文叫“弹性布局“,弹性就意味着可以延伸也可以收缩。

要实现flex item的弹性效果,还需要借助三个属性:

  • flex-grow:表示flex item的增长因子。决定了某个flex item相对于其他flex item如何分配剩余空间,默认为0。
  • flex-shrink:表示flex item的收缩因子。当容器尺寸不够容纳所有的flex item时,决定了某个flex item相对于其他flex item如何收缩自身空间,默认为1。注意:flex item不会收缩到0
  • flex-basis:flex的初始基础尺寸。默认为auto(如果同时设置了with、height属性和flex-basis非auto的值,那么flex-basis具有更高的优先级)。

另:如果flex-basis设置为0表示该元素的初始尺寸为minimum-size(最小尺寸):

  • 对于内容为英文的盒子,最小尺寸为整个句子中最长的单词所占据的宽度
  • 对于中文,最小尺寸为单个字占据的宽度

image.png

以上三个属性需要设置给flex item元素,也可以简写为flex:flex-grow flex-shrink flex-basis;

flex item的默认弹性设置为:flex:0 1 auto;,表示不延伸、等比例收缩。

再讲排列方向和对齐

我们已经知道了通过flex-direction来指定flex主轴上排列的方向,其实它的值有多种:

  • row:水平方向,从左到右(默认值)
  • column,竖直方向,从上到下
  • row-reverse:水平方向,从右到左
  • column-reverse:竖直方向,从下到上

所以,flex-direction只是指定了一个大概的排列方向,那么flex item具体如何对齐,仍需要通过额外的属性来指定,主要有两个方面:

  • 主轴的对齐:通过justify-content

image.png

  • 交叉轴的对齐:通过align-items(设置到flex容器上)或者align-self(设置到flex item上)。二者本质上是一样的,当需要给flex-item设置相同的对齐规则时,建议使用align-items

image.png

独立独行的内、外边距

首先,flex容器的外边距不会与flex item的外边距“合并”,也就是说没有外边距的塌陷问题。

其次,flex item各自的外边距和内边距相互独立不会互相影响。

再次,flex item内、外边距的百分比值都是相对于flex容器的width值的,无论其主轴是什么方向。

另:值为auto的外边距可以吸纳额外的有效空间,从而实现元素的布局的效果。

快速实现一个左右布局

这里利用了margin-left吸纳了额外的有效空间,从而将第二个元素到最右边:

<style>
    .container{
        display: flex;
    }
    .right{
        margin-left: auto;
    }
</style>
<div class="container">
    <div class="left">Left</div>
    <div class="right">Right</div>
</div>

image.png

实现一个对角布局

这里利用margin-top吸纳了交叉轴的可用空间,从而将第二个元素到了最底部:

<style>
    .container{
        display: flex;
        width: 200px;
        height: 200px;
        border: 1px solid orange;
    }
    .item{
        width: 100px;
        height: 100px;
        background-color: #ccc;
    }
    .right-bottom{
        margin-top: auto;
    }
</style>
<div class="container">
    <div class="item">Left-Top-Corner</div>
    <div class="item right-bottom">Right-Bottom-Corner</div>
</div>

image.png

使用margin:auto填充空间的优点是智能、简单、便捷。

介绍几个flex的应用场景

这里再介绍几个flex有趣的案例

不定宽的手风琴侧边栏

这里的不定宽指的是容器的宽度由不确定的内容决定。hover父菜单显示其下的子菜单,你可能很快就写出了下面的代码:

<style>
    .container{
        display: inline-flex;
        flex-direction: column;
        padding: 10px;
        border: 1px solid #ccc;
    }
    .item{
        padding: 6px 0;
    }
    .item:hover + .subitem{
        display: block;
    }
    .subitem{
        display: none;
    }
</style>
<div class="container">
    <div class="item">Home</div>
    <div class="subitem">
        <div>News</div>
        <div>Advertisement</div>
        <div>Cooperation</div>
    </div>
</div>

由于我们是通过修改元素的display属性显示或隐藏子菜单,这样的切换会改变父盒子的尺寸,因此界面会明显地闪烁(重绘重排?)

image.png

我们可以借助flex item的预渲染能力,修改一下代码:

.item:hover + .subitem{
    visibility: visible;
    height: auto;
}
.subitem{
    visibility: collapse;
    height: 0;
}

image.png

这样虽然子菜单隐藏了,但是flex容器依然为它保留了布局的空间。这样子菜单显示时可以有效地避免父盒子尺寸变化带来的闪烁问题

绘制简单柱形图

直接上代码:

<style>
    .container{
        display: inline-flex;
        align-items: flex-end;
    }
    .item{
        width: 20px;
        background-color: rgb(111, 186, 248);
        margin: 0 10px;
    }
    .bar1{
        height: 20px;
    }
    .bar2{
        height: 30px;
    }
    .bar3{
        height: 50px;
    }
</style>
<div class="container">
    <div class="item bar1"></div>
    <div class="item bar2"></div>
    <div class="item bar3"></div>
</div>

image.png

我们通过设置交叉轴的对齐为终点对齐,实现圆柱底部对齐。

布局聊天框

聊天框需要实现以下几个功能点:

  • 聊天内容盒子尺寸不定
  • 从上而下排列方向
  • 一个左对齐,另一个右对齐
  • 某一方可能连续发多条消息

基于以上需求,使用普通布局方式实现上述业务可能会麻烦一点,然而使用flex布局仅需要几行代码:

<style>
    .container{
        display: flex;
        flex-direction: column;
        width: 400px;
        padding: 10px;
        border: 2px dashed orange;
    }
    .item{
        border-radius: 4px;
        border: 1px solid #ccc;
    }
    .customer{
        align-self: flex-start;
    }
    .service{
        align-self: flex-end;
    }
</style>
<div class="container">
    <div class="item customer">
        你们的软件怎么回事啊?
    </div>
    <div class="item service">
        抱歉给您带来了不便,请问是什么问题呢?
    </div>
    <div class="item customer">
        什么问题?
    </div>
    <div class="item customer">
        你知道给我带来了多大的损失吗?
    </div>
</div>

image.png

总结

当然,flex布局的知识不仅仅这些,还有更多精彩有趣的等待着我们发现。

我们可以对flex布局做个总结:

  • 可以设置任意的流动方向(左,右,上,下)
  • 可以反转方向
  • 主轴可以单行或多行排列(支持折行)
  • 可以根据设置“弹性”地伸缩自己的尺寸适应flex容器的大小
  • 支持主轴、交叉轴的多种对齐方式

最后,由于篇幅所限,无法对flex所有的知识点都解释到,有对flex任何疑惑的小伙伴,欢迎留言讨论哦。

感谢阅读