Flex Layout 详解

2019/5/7号二次更新:为折叠的flex item的例子增添了codePen连接。
2019/5/7号更新:补充了总体结构,若有疑问,欢迎留言。
css


总想写flex,却一直拖——拖——拖,今天算是写完了。与以往的文章不一样,这篇文章更像是写给本身看的,因此有些术语不会再解释,如看不懂,能够先去这篇文章里看看术语.
html

总体结构

咱们先看这个例子浏览器

div.parent {
display: flex;
width: 1000px;
height:400px;
border: solid 2px rgba(75, 234, 12, 0.7);
border-radius: 8px;
}
div.parent > div {
border-radius: 8px;
flex: 1 1 auto;
color: #fff;
text-align: center;
line-height: 200px;
font-size: 26px;
}
div.first-child {
width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
}
div.second-child {
width: 400px;
height: 200px;
background-color: rgb(255, 141, 75);
}
复制代码
<div class="parent">
<div class="first-child">first-child</div>
<div class="second-child">second-child</div>
</div>
复制代码

效果以下:网络

上面的父级box是个flex container, 有两个flex item(div.first-child和div.second-child)。 dom

flex direction默认为row,那么横坐标轴称做main axis,flex container(flex item)在main axis方向的尺寸(即宽度)称做flex container(flex item)的main size, flex container的main size方向的起点叫main start,终点叫main end。post

纵坐标轴称做cross axis,,flex container(flex item)在cross axis方向的尺寸(即高度)称做flex container(flex item)的cross size。flex container的cross size方向的起点叫cross start, 终点叫cross end。性能

在flex container里,flex item 会沿着main start依次排列,直至排到main end。接着换行,从cross satrt 开始往cross end方向排列。 字体

总体结构就大体如上,接下来会逐一讲解flex container,flex item,以及它们之间的渲染原理。flex

Flex Container

  1. display为flex的元素会产生一个flex container,该container在正常流内以block-level的形式存在。ui

  2. display为inline-flex的元素会产生一个flex container,该container在正常流内以inline-level的形式存在。

  3. 每一个flex container都会为它的内容创建一个flex formatting context,这与block container为它的内容创建一个block formatting context很相似。可是,flex container并非block container, 诸如:

  • float和clear在flex container里面无效(也就是flex item即便设置了float和clear也不起做用)

  • vertical-align在flex item上也不起做用

  • flex container的margin不会与它的内容的margin重叠

  • ::first-line和::first-letter伪元素在flex container上不起做用


注意:当元素为绝对定位元素、浮动元素或根元素时,display属性值的computed value的计算规则参照下表
Specified value Computed value
inline-table table
inline, table-row-group, table-column, table-column-group, table-header-group, table-footer-group, table-row, table-cell, table-caption, inline-block block
inline-flex flex
others same as specified

Flex Item

简单的说,flex container的每一个孩子都是一个flex item,而每一个flex item都是一个flex-level box,另外,因为在计算computed value时flex item会发生块级化,所以每一个flex item也是一个block container。而对于没有html元素包裹的连续文字,会被自动包裹在一个匿名的block container flex item里。

释1: 块级化 —— 尽管有的flex item的display属性值为inline,可是在计算computed value时,也会被设为block.

释2: block container ——该box只能包含block-level box或是创建一个inline formatting context且只能包含inline-level box

1. flex item为绝对定位元素

由于为绝对定位元素,因此该flex item会脱离流。效果就如同该flex item的static position box(在计算绝对定位元素位置时,会先假设它的position为static,处于正常流中,而后得出一个static position,再依据这个static position去定位。)为flex container的content box。也就是该flex container里面只有该flex item的static position box,这个flex container是个匿名的flex container。

例:为div.first-child加上绝对定位。div.first-child改成:

div.first-child {
Width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
position: absolute;
}
复制代码

效果以下:

选中div.second-child能够看到,它的宽度独占1000px。

若是给绝对定位flex item加上align-self: center,它会处于它所在的flex container的交叉轴的中心位置。修改div.first-child以下:

div.first-child {
width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
position: absolute;
align-self: center;
}
复制代码

效果以下:

而一旦为绝对定位flex item设置了top/bottom,它就会参照第一个父级定位元素去移动,align-self也就失效了。如今设置div.parent为相对定位元素,修改为:

div.parent {
display: flex;
Width: 1000px;
height:400px;
border: solid 2px rgba(75, 234, 12, 0.7);
border-radius: 8px;
position:relative;
}
复制代码

为div.first-child设置偏移量,修改以下:

div.first-child {
width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
position: absolute;
align-self: center;
left: 50px;
top: 50px;
}
复制代码

效果以下:

对比上图,能够看到交叉轴上div.first-child距离div.parent的上边缘只有上图的一半,也就是50px,同时div.first-child距离div.parent的左边缘也是50px。

2. flex item的margin和padding

  • 相邻的flex item的margin不会重叠(flex container的margin也不会与flex item的margin重叠)。
  • margin和padding若设置为百分比,则百分比是基于flex container的inline size(若writing mode是horizontal,则inline size为宽度;若mode是vertical,则inline size 为高度 )。

例子:
div.parent {
display: flex;
flex-wrap: wrap;
width: 400px;
height: auto;
background-color: rgba(15, 241, 170, 0.42);
border-radius: 8px;
margin-top: 50px;
}
div.parent > div {
border-radius: 8px;
flex: 1 1 auto;
color: #fff;
text-align: center;
line-height: 200px;
font-size: 26px;
}
div.first-child {
width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
margin-top: 50px;
margin-bottom: 50px;
}
div.second-child {
width: 400px;
height: 200px;
background-color: rgb(255, 141, 75);
margin-top: 50px;
}
复制代码

效果以下:

能够看到div.parent距离root box的上边缘有50px,而div.first-child的上边缘距离div.parent的上边缘也有50px,他们并无重叠。div.first-child及div.second-child的margin-top也没有重叠。

若是div.parent的display为block,而且div.parent及div.first-child的border宽度均为0,它和div.first-child的margin-top会发生重叠。修改div.parent以下:

div.parent {
display: block;
width: 400px;
height: auto;
background-color: rgba(15, 241, 170, 0.42);
border-radius: 8px;
margin-top: 50px;
}
复制代码

效果以下:

鼠标定位到div.parent上,能够看到它的margin-top和div.first-child的margin-top确实重叠了。同理,div.first-child与div.second-child的margin-top也重叠了。

3. 折叠的flex item

  1. Flex item能够设置visibility: collapse,达到折叠的效果。效果相似于table的行折叠。
  2. Flex item即便折叠了,但依然有个隐形的空壳存在,为的是保证flex container的cross size的稳定。若是flex container仅有一个flex line(每条flex line上按main size方向依次摆放flex item,相似于line box),那么某个flex item折叠可能会影响flex container 的main size,可是不会影响flex container的cross size。
  3. 尽管折叠的flex item不会被渲染,但依然会出如今dom 树上。为了计算折叠的flex item的那个空壳的大小,在dom树造成的过程当中,flex layout 会先假设全部item都没有折叠,而后在由dom树造成渲染树的过程当中,会将折叠的flex item用一个保留了原始的cross size的空壳替换掉。

例子:

div.collapse-example {
  display: flex;
  > ul.nav {
    height: auto;
    margin: 0;
    margin-right: 20px;
    > li {
      margin-bottom: 5px;
      border-radius: 4px;
      text-align: center;
      font-size: 18px;
      display: flex;
      flex-flow: column;
    }
    > li:target,
    > li:hover {
      cursor: pointer;
      > a {
        border-top-left-radius: 8px;
        border-top-right-radius: 8px;
        font-weight: bold;
      }
      > ul {
        border-radius: 8px;
      }
      a {
        color: #343434 !important;
      }
    }
    > li:not(:target):not(:hover) > ul {
      height: 0;
      overflow: hidden;
      visibility: collapse;
    }
  }
  ul {
    padding: 0 10px;
    > li {
      list-style: none;
    }
  }
}
复制代码
<div class="collapse-example">
        <ul class="nav">
          <li>
            <a href="#">About</a>
            <ul>
              <li>
                <a href="#">introduction</a>
              </li>
              <li>
                <a href="#">blog</a>
              </li>
            </ul>
          </li>
          <li>
            <a href="#">Product</a>
            <ul>
              <li>
                <a href="#">book</a>
              </li>
              <li>
                <a href="#">beautiful-closes</a>
              </li>
            </ul>
          </li>
          <li>
            <a href="#">Contact</a>
            <ul>
              <li>
                <a href="#">email</a>
              </li>
              <li>
                <a href="#">weixin</a>
              </li>
            </ul>
          </li>
        </ul>
        <article id="main">
            This is an Example 
        </article>
      </div>
复制代码

效果以下:

鼠标移到菜单上,子菜单会显示出来。( 点击连接看看

4. flex item的automatic minimum size

min-width和min-height有了一个新的初始化值,即auto,表示自动设置的min size。

对于flex item,当min-width或min-height的specified value设置为auto,相应的used value是基于内容来设置的。这个值怎么定呢?

对于min-width,通常,取主轴上width的specified value与主轴上min-content size(一个box的最小size,该size不会致使overflow)的较小值; 若是主轴上width没有specified value, 可是有个ratio且交叉轴上width设置了specified value,则根据ratio及交叉轴width的specified value得出一个转换后的size,再取这个size和主轴上min-content size的较小值;若是既没有设定主轴width的specified value有没有ratio,那么直接取主轴上min-content size。

同理min-height。

一般,automatic minimum size取值合理,可是若是是如下状况,仍是为min size设置一个具体的值比较好。

  1. 当flex item的content是一篇较大的文档,给min-width或min-height设置成字体相关的值比较好,例如min-width: 12em。使用 automatic minimum size可能会致使溢出。
  2. 当flex item有较多的孩子节点时,automatic minimum size的取值须要基于min-content size, 而 layout engine为了找到min-content size须要一一遍历全部的孩子节点,有损性能。若是直接给min size设置specified value,就没必要去找min-content size了。
注意:ratio表示一个元素的width与height的比例关系,通常经过给padding-top设定百分比来表达一个固定比例。(由于padding的百分比值是参考containing box的宽度的)
aspect ratio padding-top value
1:1 100%
16:9 56.25%
4:3 75%
3:2 66.66%
8:5 62.5%

定向和排序

flex container的内容能够按照必定的方向和顺序排列。主要依靠flex container的flex-direction和flex-wrap以及flex item的order。

可是有点需切记,不可依靠flex-direction/flex-wrap 的-reverse值 以及order来代替flex item的本来顺序,会影响页面的可访问性。

1. flex-direction

Name: flex-direction
Value: row | row-reverse | column | column-reverse
Initial: row
Applies to: flex containers
Inherited: no
Percentages: n/a

2. flex-wrap

Name: flex-wrap
Value: nowrap | wrap | wrap-reverse
Initial: nowrap
Applies to: flex containers
Inherited: no
Percentages: n/a

flex: nowrap表示flex container为单行,内容大小超出则调整flex item的大小以避免溢出。

flex: wrap 表示在内容过多时flex container会换行

flex: wrap-reverse 表示在内容过多时flex container会换行,但cross-start及cross-end方向调换。

3. Order

Name: order
Value:
Initial: 0
Applies to: flex items
Inherited: no
Percentages: n/a

order能够设置flex item的位置。 这些flex item会在文档顺序的基础上再基于order属性值进行再排序,而后按照再排序的结果渲染出来。若是某些flex item的order值相同,则按照它们在文档里的顺序渲染。

例子:
div.parent {
display: flex;
width: 400px;
height: auto;
border: solid 2px rgba(15, 241, 170, 0.42);
border-radius: 8px;
}
div.parent > div {
border-radius: 8px;
flex: 1 1 auto;
color: #fff;
text-align: center;
line-height: 200px;
font-size: 26px;
}
div.first-child {
width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
}
div.second-child {
width: 400px;
height: 200px;
background-color: rgb(255, 141, 75);
order: -1;
}
复制代码

效果以下:

灵活性

1. flex-grow

当一个flex line还有多余的空间可扩展,这些空间如何分配给该行的flex item呢?用flex-grow, 它用来设置某个flex item可扩展多少比例的多余空间。取值为number,默认为0。

2. flex-shrink

当一个flex line的空间不足,这些缺失的空间如何分担给该行的flex item呢?用flex-shrink,它表示某个flex item须要缩小多少比例的空间,取值为number,默认为1。

3. flex-basic

定义了分配多余空间(可为正数亦可为负数)前该flex item所占的main size,浏览器会根据该值来计算主轴上的多余空间。可设定以下属性值:

  • auto: 当specified value为auto时,按以下步骤取值。 第一步:采用的used value为flex item的main size(即主轴上的width或height的属性值), 第二步:若是这个used value依然为auto,那么used value 会基于 flex item的content 获得一个具体值。
  • content: 基于flex item的content获得一个automatic size。
  • <width>: 和width或height的设置方式同样,可设为<length> 或 <percentage>

默认值为auto。当主轴为水平方向,设置了flex-basic,则flex item的width值会失效。例如,若是某个flex item的flex-basic设为0,则把该flex item的宽度视为0,即便它自己width为100px,这个100px也会被归入多余空间中,供flex inline的全部flex item一块儿分配。

4. flex

flex属性为flex-grow flex-shrink flex-basic的缩写,默认值为0 1 auto,可设定的值为:

  • initial: 0 1 auto,当有多余空间时,没有弹性;当空间不足时,flex item能够缩小。
  • auto: 1 1 auto,flex会有充足的弹性
  • none: 0 0 auto, flex会彻底没有弹性
  • <positive-number>: <positive-number> 1 auto
  • <flex-grow> <flex-shrink> <flex-basic>: 若flex-grow省略了,默认值取1;若flex-shrink省略了,默认值取1;若flex-basic省略了,默认值取0。

一般,flex item不会缩小到比min content sizes还要小。为防万一,咱们能够为flex item设置min-width或min-height。


对齐

1. 使用auto margin对齐

  • 若是设置了flex-grow,auto margin 会被设置为0;若设置了flex-basis,没有设置flex-grow,那么在主轴方向上,根据flex-basis计算出的多余的空间都会分配给auto margin
  • auto margin的优先级高于justify-content及align-self,轴上任何多余的空间(不包括负的空间)都会分配给auto margin。
  • 若是box在某个轴上发生溢出了,则auto margin会被忽略,且box在该轴的尾部溢出。(若是既有auto margin, 又有justify-content/align-self/align-items,依然是忽略justify-content/align-self/align-items,采用auto margin,只不过此时由于overflow,接着会忽略auto margin, 在轴的尾部溢出)

总的来讲,当存在auto margin时,又有多余的空间(不包括负的空间),则优先级以下:

flex-grow > auto margin > justify-content/align-self/align-items

若是box在轴上只剩下负的空间(即溢出),则auto margin被忽略。

例子:
div.parent {
display: flex;
flex-wrap: nowrap;
justify-content: center;
width: 1000px;
height: auto;
border: solid 5px rgba(15, 241, 170, 0.42);
border-radius: 8px;
}
div.parent > div {
border-radius: 8px;
flex: 1 1 auto;
color: #fff;
text-align: center;
line-height: 200px;
font-size: 26px;
}
div.first-child {
width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
margin-left: auto;
}
div.second-child {
width: 400px;
height: 200px;
background-color: rgb(255, 141, 75);
}
复制代码

如今div.parent的justify-content设为center, div.first-child及div.second-child设置了flex: 1 1 auto(其中flex-grow: 1, flex-shrink: 1, flex-basis: auto); 同时div.first-child设置了margin-left为auto,效果以下:

能够看到div.first-child及div.second-child均扩展了,justify-content及auto margin没起做用。

如今设置div.first-child及div.second-child的flex为0 1 auto,修改以下:

div.parent > div {
border-radius: 8px;
flex: 0 1 auto;
color: #fff;
text-align: center;
line-height: 200px;
font-size: 26px;
}
复制代码

效果以下:

能够看到,剩余空间都被div.first-child的auto margin-left 占据了。

如今去掉div.first的auto margin,修改以下:

div.first-child {
width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
}
复制代码

效果以下:

如今,justify-content起做用了。

2.justify-content对齐

justify-content用来设置flex-item在主轴上的对齐方式。

Name: justify-content
Value: flex-start | flex-end | center | space-between | space-around
Initial: flex-start
Applies to: flex containers
Inherited: no

效果以下:

3. align-items和align-self

align-items定义了flex container中flex-item在交叉轴上的对齐方式,有点相似justify-content,是针对全部flex item。

align-self定义了某个flex item在交叉轴上的对齐方式,会覆盖align-items的值。

Name: align-items
Value: flex-start | flex-end | center | baseline | stretch
Initial: stretch
Applies to: flex containers
Inherited: no

Name: align-self
Value: auto | flex-start | flex-end | center | baseline | stretch
Initial: auto
Applies to: flex items
Inherited: no

  • auto: 对于flex-item,能够设置align-self为auto, auto值的computed value会设置为flex container的align-items的值。
  • center: flex item的margin box 在交叉轴方向上处于flex line的中间位置。若是flex line的高度比flex item的还要低,则flex item在交叉轴的首尾两端溢出相等部分。
  • baseline: flex item会依照flex line的baseline对齐。在交叉轴方向上,baseline与flex item的margin box的上边缘距离最远的那个flex item会被放置在交叉轴的开始位置。
  • stretch: 若flex item的 cross size的computed value为auto(若是flex-direction为row,那么cross size就是flex item的height),且交叉轴方向的margin均不为auto,那么align-items/align-self为stretch时,flex item会被拉伸。flex item的cross size的used value会尽量的接近flex line的高度,若是flex item设置了min(max)-width(height),那么这个used value会受它们的限制。
效果以下图:

4. align-contents

Name: align-content
Value: flex-start | flex-end | center | space-between | space-around | stretch
Initial: stretch
Applies to: multi-line flex containers
Inherited: no

align-content定义了flex line在flex container的交叉轴方向上的对齐方式,相似于justify-content,都属于flex container属性。只不过它要对齐的对象是flex line,且在交叉轴方向,而justify-content要对齐的对象是flex item,在主轴上。

属性值以下:

space-around: flex container中的flex line之间的空间相等,第一个flex line与 flex container的margin box的上边缘之间的空间只有flex line之间的空间的一半。

效果以下:

注明:本篇文章中少数图片来源网络,若有侵权,请当即联系本人删除。
相关文章
相关标签/搜索