1

本文译自 A Complete Guide to Flexbox
这里,针对本文介绍的属性列个提纲:
伸缩容器属性

  • display

  • flex-direction

  • flex-wrap

  • flex-flow

  • justify-content

  • align-items

  • align-content

伸缩项目属性

  • order

  • flex-grow

  • flex-shrink

  • flex-basis

  • flex

  • align-self

以后再布局时可以考虑用flexbox啦~~

背景

Flexbox 布局(Flexible Box)模块(目前W3C上一次工作草案的叫法)旨在提供一种更高效的方式来布局、排列及分配容器中项目的空间,即便容器大小是未知或动态变化的(因此称为“flex”)。

Flex布局的主要思想是使容器具备改变其子项目的宽度或高度的能力,以此充分填充可用空间(主要是为了适应各种显示设备和屏幕尺寸)。使用flex布局的容器能扩展其子项目来填满可用空间,也能缩小他们以防止溢出容器。

Flex布局与常规布局相比,最主要的是它是方向无关的(常规布局通常是块级元素从上到下布局,行内元素从左到右布局)。尽管常规布局对于页面布局十分好用,但却缺少对大型或复杂应用的灵活性支持(尤其是当涉及到取向改变,尺寸调整,拉伸,收缩等场景时)。

注意:Flexbox布局主要适用于应用的组件以及小规模的布局,对于那些较大规模的布局网格布局更适用。

基本概念&术语

Flexbox是一整个模块,并非单一的一个属性,它涉及的东西比较多,包括一系列属性。其中一些属性是用在容器(父元素,即伸缩容器)上的,其他一些属性则是用在子元素(伸缩项目)上的。

如果说常规布局是基于块与行内元素的流向的,那么flex布局则是基于“flex流向”的。请看来自w3c规范中的这张图,解释了flex布局的主要思想:
W3C flex

通常,伸缩项目的布局要么按照主轴(main axis)方向,从主轴起点(main-start)到主轴终点(main-end),要么按照交叉轴(cross axis)方向,从交叉轴起点(cross-start)到交叉轴终点(cross-end)。

  • 主轴(main axis) - 伸缩容器的主轴,是伸缩项目布局遵循的主要方向。注意:这个方向不一定是水平方向;它取决于“flex-direction”属性的值(见下文);

  • 主轴起点(main-start) | 主轴终点(main-end ) - 伸缩项目置于容器中,从主轴起点(main-start)到主轴终点(main-end);

  • 主体大小(main size) - 伸缩项目的宽度或高度就是主体大小,不管主维度是哪个。伸缩项目的主体大小要么是‘width’属性,要么是‘height’属性,不管主维度是哪一个。

  • 交叉轴(cross axis) - 与主轴垂直的轴称为交叉轴(cross axis), 它的方向取决于主轴(main axis)的方向;

  • 交叉轴起点(cross-start) | 交叉轴终点(cross-end) - 伸缩行填充着伸缩项目,并放置于伸缩容器中,从伸缩容器交叉轴起点开始,沿着交叉轴终点方向;

  • 交叉方向大小(cross size) – 伸缩项目的宽度或高度就是交叉方向的大小,不管交叉维度是哪一个。交叉方向大小属性要么是交叉轴维度的宽度要么是交叉轴维度的高度。

父元素(伸缩容器)属性

image

display

display属性用来定义伸缩容器,容器是行内元素或者块元素取决于给定的值,这个属性开启了容器内所有直接子元素的flex上下文。
CSS:

.container {
    display: flex; /*or inline-flex*/
}

注意:CSS columns对于伸缩容器没有作用。

flex-direction

image

这个属性建立了主轴方向,定义了伸缩项目在伸缩容器内排列的方向。Flexbox是一个单一方向布局的概念。可以认为伸缩项目或者沿水平方向或者沿垂直方向布局。
CSS:

.container {
    flex-direction: row | row-reverse | column | column-reverse;
}
  • row(默认值): 在‘ltr’排版中从左到右,‘rtl’排版中从右到左;

  • row-reverse: 'ltr'中从右到左,‘ltr’中从左到右;

  • column: 与row属性一样,但是是从顶部到底部;

  • column-reverse: 与row-reverse一样,但是是从底部到顶部。

flex-wrap

image

默认情况下,伸缩项目会尽量排成一行。当然,可以根据需要让伸缩项目换行来改变它。此时,方向也很重要,它决定了下一行堆叠的方向。
CSS:

.container {
    flex-wrap: nowrap | wrap | wrap-reverse;
}
  • nowrap(默认):单行模式/'ltr'中从左到右,'rtl'中从右到左;

  • wrap: 多行模式/'ltr'中从左到右,'rtl'中从右到左

  • wrap-reverse: 多行模式/'ltr'中从右到左,'rtl'中从左到右;

flex-flow(适用于:伸缩容器,也就是父元素)

这是'flex-direction'和 'flex-wrap'属性的快捷形式,它同时定义了伸缩容器的主轴和交叉轴。默认值是row nowrap。
CSS:

flex-flow: <'flex-direction'> || <'flex-wrap'>

justify-content

image

justify-content定义了沿着主轴的排列方式。当一行中的所有伸缩项目都不能再伸缩时,或者能伸缩但已经达到了自身最大尺寸时,这个属性负责分配剩余的自由空间。同时它也能在溢出时对排列元素进行一定的控制。
CSS:

.container {
    justify-content: flex-start | flex-end | center | space-between | space-around
}
  • flex-start(默认值):伸缩项目朝着起点方向紧凑排列;

  • flex-end: 伸缩项目朝着终点方向紧凑排列;

  • space-between: 伸缩项目在一行中平均分配,第一个项目在起点处,最后一个项目在终点处;

  • space-around: 伸缩项目两侧的空间保持相等,在一行中平均分配。值得注意的是在视觉上,一个项目两边的空间是不相等的,因为所有项目都是在自身的两边分配了相等的空间。第一个项目在容器边缘分配得到一份空间,但是第二个项目的左边会分得两份空间,因为它自身的也有一份分配的空间。

align-items

image

align-items定义了伸缩项目沿着交叉轴当前方向的布局方式。可以把它看成交叉轴方向的justify-content版本。
CSS:

.container {
    align-items: flex-start | flex-end | center | baseline | stretch;
}
  • flex-start: 伸缩项目的起点放置在交叉轴线的起点上;

  • flex-end: 伸缩项目的终点放置在交叉轴线的终点上;

  • center: 伸缩项目置于交叉轴线的中心;

  • baseline: 伸缩项目按照他们的基线排列;

  • stetch(默认): 拉伸以填满容器(仍然遵循min-width/max-width)

align-content

image

align-content定义了伸缩容器当在交叉轴上有多余空间时的排列线,和主轴方向上justify-content排列伸缩项目类似。
注意:这个属性在只有一行伸缩项目时无效。
CSS:

.container {
    algin-content: flex-start | flex-end | center | space-between | space-around | stretch;
}
  • flex-start: 紧邻容器起始排列;

  • flex-end: 紧邻容器终点排列;

  • center: 排列在容器中间;

  • space-between: 平均分配,第一行在容器起始处而最后一行在容器末尾处;

  • space-around: 平均分配,每行两边距离相等;

  • stretch(默认值): 拉伸项目来占据剩余空间;

子元素属性(伸缩项目)

image

order

image

默认情况下,伸缩项目按照文档流出现的先后顺序排列。但order属性可以控制在伸缩容器内的显示顺序。
CSS:

.item {
    order: <integer>;
}

flex-grow

image

flex-grow定义了伸缩项目必要时扩展的能力。它接受一个无单位的值作为比例。它指定了伸缩容器内伸缩项目能伸展的可用空间的大小。

如果所有伸缩项目的flex-grow属性设置为1,容器中剩余空间会被均等分配给所有子元素。如果一个子元素的flex-grow设置为2,那么它占据的剩余空间中会是其他子元素占据空间的两倍。
CSS:

.item {
    flex-grow: <number>; /*default 0*/
}

负数无效。

flex-shrink

flex-shrink定义了伸缩项目根据需要收缩的能力。

.item {
    flex-shrink: <number>; /*default 1*/
}

负数无效。

flex-basis

定义了元素在剩余空间被分配之前的默认大小。它的值可以是长度(例如20%, 5rem等),也可以是关键词。'auto'表明元素的大小取决于元素的宽度或高度属性(临时由main-size属性决定)。'content'表明元素大小取决于伸缩项目内容的大小-目前尚不支持,因此max-content,min-content,fit-content这些兄弟属性较难测试和分析。
CSS:

.item {
    flex-basis: <length> | auto; /*default auto*/
}

如果设置为0,内容区域周围的多余空间将不被考虑,如果设置为auto, 多余空间会基于flex-grow值来分配。参考下图。
image

flex

这是结合flex-grow,flex-shrink,flex-basis属性的简易写法。第二个属性flex-shrink和第三个属性flex-basis是可选的。默认值是0 1 auto;

.item {
    flex:none | [ <'flex-grow'> <'flex-shrink'> ? || <'flex-basis'> ]
}

推荐使用这个快捷形式的属性,比逐个设置属性好用。它能自动设置所有属性的值。

align-self

image

允许默认对齐方式(或者由align-items定义的对齐方式)能被单个伸缩项目覆盖。
CSS:

.item {
    align-self: auto | flex-start | flex-end | center | baseline | stretch;
}

注意:float,clear,vertical-align属性对伸缩项目没有作用。

Examples

我们先从一个非常简单的例子开始,解决一个最常见的问题:居中。使用flexbox你会发现简直不能更容易了。
CSS:

.parent {
    display: flex;
    height: 300px; /*Or whatever*/
}
.child {
    width: 100px; /*Or whatever*/
    height: 100px; /*Or whatever*/
    margin: auto; /*Magic! */
}

这依赖于一个特性:伸缩容器里的元素的margin值如果设置了'auto',能自动均衡容器中剩余的空间。所以在垂直方向也设置了auto后,能使得伸缩项目在两个轴线方向上都完美居中。

现在,让我们来使用更多属性。考虑有一个有6个条目的列表,每一个都是固定大小的,但他们可以被调整大小的。我们希望它们能平均地分布在水平轴上,这样当我们调整浏览器时,依然能良好地显示(不使用媒体查询)。

.flex-container {
    /*首先创建一个伸缩容器*/
    display: flex;
    /*然后定义伸缩流方向,并且伸缩项目能换行
    * 记住这个设置等同于下面这两个设置:
    * flex-direction: row;
    * flex-wrap: wrap;
    */
    flex-flow: row wrap;
    /*然后定义剩余空间如何分配*/
    justify-content: space-around;
}

好了。剩下的仅仅是样式的问题了。
下面是一个阐述这个属性的具体例子。试试去CodePen上查看,再调整浏览器窗口看看会发生什么。
HTML:

<ul class="flex-container">
  <li class="flex-item">1</li>
  <li class="flex-item">2</li>
  <li class="flex-item">3</li>
  <li class="flex-item">4</li>
  <li class="flex-item">5</li>
  <li class="flex-item">6</li>
</ul>

SCSS:

@import "compass/css3";

.flex-container {
  padding: 0;
  margin: 0;
  list-style: none;
  
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;
  
  -webkit-flex-flow: row wrap;
  justify-content: space-around;
}

.flex-item {
  background: tomato;
  padding: 5px;
  width: 200px;
  height: 150px;
  margin-top: 10px;
  
  line-height: 150px;
  color: white;
  font-weight: bold;
  font-size: 3em;
  text-align: center;
}

这是在PC正常显示屏幕下的效果(浏览器宽度约为1190px)的效果:

flexbox-eg1.jpg

缩小浏览器,6个盒子的排列会随浏览器宽度的大小进行调整,这是浏览器宽度小于约700px时的效果:

flexbox-eg2.jpg

随着浏览器继续缩小,最终6个盒子会变为逐个纵向排列的布局形式。

再试试别的属性。设想网站顶部有一个靠右对齐的导航,我们希望它在中等大小的屏幕和单一栏目的小屏幕上都居中显示。非常容易:
CSS:

/* Large */
.navigation {
  display: flex;
  flex-flow: row wrap;
  /* This aligns items to the end line on main-axis */
  justify-content: flex-end;
}

/* Medium screens */
@media all and (max-width: 800px) {
  .navigation {
    /* When on medium sized screens, we center it by evenly distributing empty space around items */
    justify-content: space-around;
  }
}

/* Small screens */
@media all and (max-width: 500px) {
  .navigation {
    /* On small screens, we are no longer using row direction but column */
    flex-direction: column;
  }
}

具体示例代码:
HTML:

<ul class="navigation">
  <li><a href="#">Home</a></li>
  <li><a href="#">About</a></li>
  <li><a href="#">Products</a></li>
  <li><a href="#">Contact</a></li>
</ul>

SCSS:

@import "compass/css3";

.navigation {
  list-style: none;
  margin: 0; 
  
  background: deepskyblue;
  
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;
  
  -webkit-flex-flow: row wrap;
  justify-content: flex-end;
}

.navigation a {
  text-decoration: none;
  display: block;
  padding: 1em;
  color: white;
}

.navigation a:hover {
  background: darken(deepskyblue, 2%);
}

@media all and (max-width: 800px) {
  .navigation {
    justify-content: space-around;
  }
}

@media all and (max-width: 600px) {
  .navigation {
    -webkit-flex-flow: column wrap;
    flex-flow: column wrap;
    padding: 0;
  }
  
  .navigation a { 
    text-align: center; 
    padding: 10px;
    border-top: 1px solid rgba(255,255,255,0.3); 
    border-bottom: 1px solid rgba(0,0,0,0.1); 
  }

  
  .navigation li:last-of-type a {
    border-bottom: none;
  }
}

上述示例结果图:

flexbox-eg3.jpg

缩小浏览器,宽度小于等于600px时:

flexbox-eg4.jpg

现在让我们再尝试一下伸缩项目上的属性!比如一个手机上的三栏布局,其中头部和尾部占据全屏。并且不管它们在文档流的顺序。
CSS:

.wrapper {
  display: flex;
  flex-flow: row wrap;
}

/* We tell all items to be 100% width */
.header, .main, .nav, .aside, .footer {
  flex: 1 100%;
}

/* We rely on source order for mobile-first approach
 * in this case:
 * 1. header
 * 2. nav
 * 3. main
 * 4. aside
 * 5. footer
 */

/* Medium screens */
@media all and (min-width: 600px) {
  /* We tell both sidebars to share a row */
  .aside { flex: 1 auto; }
}

/* Large screens */
@media all and (min-width: 800px) {
  /* We invert order of first sidebar and main
   * And tell the main element to take twice as much width as the other two sidebars 
   */
  .main { flex: 2 0px; }
  
  .aside-1 { order: 1; }
  .main    { order: 2; }
  .aside-2 { order: 3; }
  .footer  { order: 4; }
}

具体示例代码:
HTML:

<div class="wrapper">
  <header class="header">Header</header>
  <article class="main">
    <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>  
  </article>
  <aside class="aside aside-1">Aside 1</aside>
  <aside class="aside aside-2">Aside 2</aside>
  <footer class="footer">Footer</footer>
</div>

SCSS:

@import "compass/css3";

.wrapper {
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;  
  
  -webkit-flex-flow: row wrap;
  flex-flow: row wrap;
  
  font-weight: bold;
  text-align: center;
}

.wrapper > * {
  padding: 10px;
  flex: 1 100%;
}

.header {
  background: tomato;
}

.footer {
  background: lightgreen;
}

.main {
  text-align: left;
  background: deepskyblue;
}

.aside-1 {
  background: gold;
}

.aside-2 {
  background: hotpink;
}

@media all and (min-width: 600px) {
  .aside { flex: 1 auto; }
}

@media all and (min-width: 800px) {
  .main    { flex: 3 0px; }
  .aside-1 { order: 1; } 
  .main    { order: 2; }
  .aside-2 { order: 3; }
  .footer  { order: 4; }
}

body {
  padding: 2em; 
}

上述示例结果图:

flexbox-eg5.jpg

缩小浏览器,宽度小于等于600px时:

flexbox-eg6.jpg

Flexbox 前缀

Flexbox需要书写较多的前缀以支持大多数浏览器下的显示。这里指的不仅仅包括(在属性前)加入浏览器的前缀,实际上还包含加入其它一些不一样的属性和属性值。这是因为Flexbox规范一直在向前发展,先后创建了"old", "tweener"和"new"几个版本。

也许更好的处理办法是使用新语法,并通过Autoprefixer来运行你的CSS。

这里给出一个Sass @mixin的示例可以帮助解决一些前缀问题,或许可以给你提供一些思路:
SCSS:

@mixin flexbox() {
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;
}

@mixin flex($values) {
  -webkit-box-flex: $values;
  -moz-box-flex:  $values;
  -webkit-flex:  $values;
  -ms-flex:  $values;
  flex:  $values;
}

@mixin order($val) {
  -webkit-box-ordinal-group: $val;  
  -moz-box-ordinal-group: $val;     
  -ms-flex-order: $val;     
  -webkit-order: $val;  
  order: $val;
}

.wrapper {
  @include flexbox();
}

.item {
  @include flex(1 200px);
  @include order(2);
}

相关属性

其它资源

Bugs

Flexbox 并非没有bug. 我看过的最好的收集这方面bug的是Philip Walton and Greg Whitworth的Flexbugs。 这是一个开源的跟踪问题的地方。

浏览器支持

这个要区分一下flexbox的版本:

  • (new)表明规范上最新的语法(例如:display:flex;);

  • (tweener)表明是2011年旧版的非官方语法(例如:display: flexbox;);

  • (old)表明是2009年旧版语法(例如:display:box)。

浏览器兼容性.png

Blackberry浏览器10+支持新版语法。
更多关于如何混合使用语法来最好地让flexbox得到浏览器的支持,请参考这篇文章这篇


evelynlab
565 声望30 粉丝

do something interesting