flex设计思想和语法简介

2,773 阅读9分钟

flex弹性盒子模型是新一代的布局模式,在大部分情况下可以完全替代之前的display属性 + position属性 + float属性的布局方式,设计友好,目前兼容性已经很好了,很多主流网站都已经开始采用。本文总结一下flex的设计思想和语法,以及在实际的布局应用中为我们带来了哪些便利。

flex设计思想

我们可以仔细想一下我们之前布局的难点和麻烦的地方都有什么,我总结了几个:

  • 自适应,比如最简单的左侧是固定宽度导航栏,右侧自适应填充剩余空间。一般使用float和margin来实现或者利用BFC的特性来实现
  • 居中,特别是垂直居中,一行有多个高度不一致的项目,要垂直居中,一般结合line-height和vertical-align来实现
  • 水平等间距的多项目排列,最两端的两侧没有间距,通常使用负margin或者css选择器来实现
  • 需要解决float造成的高度塌陷问题

其实floatpositionline-heightvertical-align设计之初并不是为了应对这种复杂布局,只是比较适用于web1.0的图文布局界面。后来人以自己的聪明才智利用种种css特性,组合使用,实现了复杂的布局方案。这种布局方案相对比较复杂,需要很扎实的基本功,不是太容易理解。flex弹性布局就是为了应对这种情况而生的。

对于上述种种不便,如果让我们自己去要求有一种新的布局方案,我们会怎么要求,我希望的是这样

  • 我希望可以定义某个元素自动扩大或者自动缩小,以实现自适应
  • 我希望可以定义行或者列的排列方式,从左边开始、居中、等间隔、从右边开始等,都可以任意设置
  • 我希望设置垂直方向和设置水平方向一样简单

我的要求就这么简单满足着3条就可以解决我日常开发中的大部分问题,提高开发效率。flex的布局设计就可以轻松满足上述要求,而且还可以满足我们更多的要求。

我们来了解一下flex的设计,首先我们了解一下2个概念:

  • flex容器:凡是设置了display: flex的元素都是flex容器
  • flex项目:所有flex容器的子元素都被称为flex项目

flex 容器

flex容器默认有两个轴线分别对应水平方向和垂直方向,叫做主轴和交叉轴。

默认主轴是水平方向,交叉轴是垂直方向,但是我们也可以通过设置调换,所以在flex设计中水平方向和垂直方向是一样容易设置的。我们可以在flex容器中设置主轴方向、排列方式。容器一共有6个css属性可以设置,其实都是围绕主轴方向、排列方式设置的。

  • flex-direction: 决定主轴方向,一共有4个值
    • row(默认): 主轴为水平方向,从左到右排列
    • row-reverse: 主轴为水平方向,从右到左排列
    • column:主轴为垂直方向,从上到下排列
    • column-reverse: 主轴为垂直方向,从下到上排列

  • flex-wrap: 决定换行:

    • nowrap(默认):不换行
    • wrap: 超过主轴空间则换行
    • wrap-reverse: 超过主轴空间则换行,新行在前面
  • flex-flow: flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap

  • justify-content: 这个属性就是决定主轴方向排列方式的,水平居中,等间隔排列都可以用它来实现,应用的比较多,有6个值:

    • flex-start(默认):左对齐
    • flex-end: 右对齐
    • center: 居中
    • space-between: 两端对齐,项目间的间隔相等
    • space-around: 每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。
    • space-evenly: 项目沿主轴均匀分布。

  • align-items: 这个属性决定交叉轴方向的排列方式,可以很方便地实现垂直居中,也有5个值,以从上到下的垂直方向为交叉轴方向:
    • flex-start: 上对齐
    • flex-end: 下对齐
    • center: 居中对齐
    • stretch(默认值): 拉伸,如果未设置高度或者高度设为为auto,则填充整个容器高度
    • baseline: 项目的第一行文字的基线对齐

  • align-content: 该属性定义了主轴有多根轴线时,多根轴线在交叉轴的排列方式。只有一根轴线,就是项目没有换行时,该属性不起作用。这里比较复杂的一个地方是多轴线的问题,主轴一旦换行就会形成多个轴线,align-items仍然在每个轴线内起作用,而轴线的排列方式则由align-content来设置,和justify-content相对应。该属性有6个值,以从上到下的垂直方向为交叉轴方向:
    • flex-start:上对齐
    • flex-end: 下对齐
    • center: 居中
    • space-between: 两端对齐,项目间的间隔相等
    • space-around: 每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍
    • stretch(默认值): 轴线占满整个交叉轴,strech属性和其他属性的不同之处在于,strech会自动扩展轴线的高度,而其他属性都是以轴线实际占用的位置进行排列。具体可以看下下面的例子。

例子:

<div class="outer">
  <div class="inner">hello</div>
  <div class="inner">nihao</div>
  <div class="inner">wobuhao</div>
</div>
.outer{
  display: flex;
  background: blue;
  height: 400px;
  flex-wrap: wrap;
  align-items: stretch;
  align-content: stretch;
}
.inner{
  width: 650px;
  background: orange;
}

效果如下:

如果我们把align-content设置为space-between,则效果如下:

如果我们把align-content设置为stretch,同时align-items设置为center,则效果如下,说明align-items在单个轴线内起作用:

你可以自己去试一试!

容器上的属性都已经介绍完了,主要就是设置主轴方向和两个轴的排列方式。

flex项目

下面我们介绍一下flex项目上的属性,flex容器上主要设置了排列方式,flex项目上则主要设置了项目的大小。同样flex-项目上有6个css属性可以设置:

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

  • flex-grow: 定义项目的放大比例,该值越大,放大的比例越大,0表示不放大,默认值为0。这个属性和下一个flex-shrink很重要,需要深入理解。这个有一个很容易误解的点,就是放大比例是参考谁来放大的。注意不是参考主轴宽度,而是参考剩余宽度,也就是容器宽度减去项目总宽度。当然了,如果项目总宽度加起来比容器宽度大,那就不存在放大这一说了,有剩余空间的情况下才会去放大。假如一行有n项目,n个项目的放大比例分别为grow1,grow2...grown,则第m(m<=n)个项目的放大空间为:

    space = growm/(grow1+grow2+...grown) * (容器宽度-项目总宽度)

    由此也可以知道为什么0表示不放大,知道了明确的换算公式,我们在设置大小时才不会乱。

  • flex-shrink: 定义项目的缩小比例,该值越大,缩小的比例越大,0表示不缩小,默认为1。缩小比例的参考系则是项目总宽度减去容器宽度,也就是说容器宽度容纳不下项目了,项目才会去缩小。这里和flex-grow不一样的地方是,缩小空间不止和flex-shrink的值有关,还和项目的宽度有关。假如一行有n个项目,n个项目的flex-shrink值分别为shrink1,shrink2...shrinkn,n个项目的宽度分别为width1,width2...widthn,则第m(m<=n)个项目的缩小空间为:

    space = (shrinkm * widthm)/(shrink1 * width1 + shrink2 * width2 +...shrinkn * widthn) * (项目总宽度-容器宽度)

  • flex-basis: 定义了项目在主轴上的所占据的空间大小,默认值为auto。我们之前说的放大缩小,都要依赖于项目总宽度的计算。这个项目总宽度就是依据flex-basis算出来的。那如果项目本来就设置了width或者height,那项目宽度或者高度由谁来决定那?答案是flex-basis,flex-basis的优先级高于width或者height,但是低于min-widht或者max-height,仍受最大最小宽度的限制。比如下面的例子:

<div class="outer">
  <div class="inner">
    
  </div>
</div>
.outer{
  display: flex;
  height: 300px;
  background: blue;
}
.inner{
  width: 300px;
  flex-basis: 500px;
  background: yellow;
}

效果如下:

可以看出项目的宽度是由flex-basis决定的。另外当flex-basisauto时,则表示项目占据主轴空间的大小为项目本来的大小,此时width便会起作用。

  • flex: flex属性是flex-grow,flex-shrinkflex-basis的简写,默认值为0 1 autoflex的值使用起来比较复杂,可以看下面这段代码,基本包含所有情况。
/* Basic values */
flex: auto; /* 1 1 auto,该放大放大,该缩小缩小 */ 
flex: none; /* 0 0 auto,既不缩小,也不放大 */ 

/* One value, unitless number: flex-grow */
flex: 2;

/* One value, width/height: flex-basis */
flex: 10em;
flex: 30px;

/* Two values: flex-grow | flex-basis */
flex: 1 30px;

/* Two values: flex-grow | flex-shrink */
flex: 2 2;

/* Three values: flex-grow | flex-shrink | flex-basis */
flex: 2 2 10%;
  • align-self: 和align-items作用一样,只不过该属性设置在项目上,可以对单个项目设置,允许我们设置某些项目的排列方式和其他不同。align-self多了一个auto属性,表示继承父元素的align-items属性。