[译] 当你建立 Flexbox 布局时,都发生了什么?

快速简介: 在个人理想世界里,CSS Grid 和 Flexbox 应看成为一个总体一块儿出现,才能组成完整的网页布局系统。然而,咱们先有了 Flexbox 属性,由于它比浮动(floats)更适合用来作网格布局,因此出现了不少以 Flexbox 为基础的网格布局系统。事实上,不少人以为 Flexbox 弹性布局使人困扰或者难以理解,都是由于尝试把它做为网格布局的方法。css

在接下来的一系列文章里面,我会花一点时间来详细讲解 Flexbox 弹性布局 —— 以以前我用来理解 grid 属性的方式。咱们一块儿看看 Flexbox 设计是为了什么,它擅长处理什么,以及咱们为何不选择它做为布局的方法。在这篇文章中,咱们会详细看一下,当你在样式表添加 display: flex 的时候,究竟会发生什么。html

一个 Flex 容器,拜托了!

为了使用 Flexbox 布局,你须要一个元素做为 flex 容器,在 CSS 中,使用 display: flex前端

HTML:android

<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
</div>
复制代码

CSS:ios

body {
  padding: 20px;
  font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}

* {box-sizing: border-box;}

p {
  margin: 0 0 1em 0;
}

.container {  
  border: 5px solid rgb(111,41,97);
  border-radius: .5em;
  padding: 10px;
  display: flex;
  flex-direction: row-reverse;
}

.item {
  width: 100px;
  height: 100px;
  padding: 10px;
  background-color: rgba(111,41,97,.3);
  border: 2px solid rgba(111,41,97,.5);
}
复制代码

请看 Rachel Andrew(@rachelandrew)在 CodePen 上的这个例子:Smashing Flexbox Series 1: display: flex;git

让咱们花一点点时间来思考一下到底 display: flex 意味着什么。在 Display Module Level 3 中,display 的每个值都由两个部分构成:内部显示模式和外部显示模式。当咱们使用 display: flex 时,咱们其实定义的是 display: block flex。flex 容器的外部显示模式是 block,它在文档流中显示为正常的块级元素。内部显示模式是 flex,因此在容器内部的直接子元素按照弹性布局来排列。github

可能你以前没仔细想过,但其实已经知道了。flex 容器在页面中和其余块级元素的表现同样。若是你在 flex 容器后面紧跟一个段落,它们两个都会表现为正常的块级元素。算法

咱们也能够把容器的属性设置为 inline-flex,就和设置成 display:inline flex 同样。好比说有一个行内级别的 flex 容器,容器里还有一些参与 flex 布局的子元素。这个 flex 容器内的子元素的表现就和块级 flex 容器内的子元素表现同样,不一样之处就在于容器自己在总体布局中的表现。后端

HTML:bash

<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
</div>

<em>这个 flex 容器是行内元素,因此另外一个行内元素会紧跟它后面显示。</em>
复制代码

CSS:

body {
  padding: 20px;
  font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}

* {box-sizing: border-box;}

p {
  margin: 0 0 1em 0;
}

.container {  
  border: 5px solid rgb(111,41,97);
  border-radius: .5em;
  padding: 10px;
  display: inline-flex;
}

.item {
  width: 100px;
  height: 100px;
  padding: 10px;
  background-color: rgba(111,41,97,.3);
  border: 2px solid rgba(111,41,97,.5);
}
复制代码

请看 Rachel Andrew(@rachelandrew)在 CodePen 上的这个例子:Smashing Flexbox Series 1: display: inline-flex;

元素的外部显示模式决定了它做为盒模型在页面中怎样显示,同时与内部显示模式一块儿,决定了其子元素的行为。这是一个颇有用的思想。你能够把这种思想应用于任何 CSS 定义的盒模型。这个元素将会如何表现?它的子元素又会怎样表现?这些问题的答案就与它们的内部显示模式和外部显示模式有关。

行或者列?

一旦咱们定义了 flex 容器以后,一些默认值就开始发挥做用了。在咱们没有添加任何其余属性的状况下,flex 子项目(flex item)会按照行来排列。这是由于 flex-direction 属性的默认值就是 row。若是你不对它进行设置,它就会按照行的方向来显示。flex-direction 属性是用来设置主轴(main axis)的排列方向,这个属性还有其余的值:

  • column
  • row-reverse
  • column-reverse

当子项目排成一行的时候,子项目会按照在文档中的顺序,从行内维度的起始边缘依次排列。在规范中,这个边缘就被叫作 main-start

main-start 是一行的开始

main-start 是行内维度的起始位置(Large preview)。

若是咱们使用 column,子项目从块级维度的起始边缘开始排列,所以构成一列。

子项目按照列来排列,main-start 位于顶部

main-start 是块级维度的起始位置(Large preview)。

当咱们使用 row-reverse 时,main-startmain-end 的位置互换了,所以,子项目也会相应的按照相反的顺序来排列。

子项目从行的末尾开始排列

main-start 在内联维度的末尾(Large preview)。

column-reverse 也具备同样的效果。另外还有一点很重要,那就是这些值并不会“改变子项目的顺序”,尽管它们看起来是这样的效果,它们改变的是这些子项目开始排列的位置:经过改变 main-start 来达到目的。因此咱们的子项目会按照相反的方向来排列,这仅仅是由于它是从容器的结束位置开始排列的。

另外还有一件很重要的事情要记住,当排列顺序发生改变时,这仅仅是视觉上的,由于咱们要求子项目从结束位置开始排列。它们在文档中仍然是原来的顺序,当你使用屏幕阅读器的时候仍然是按照源文档的顺序进行索引。若是你真的想要改变子元素的顺序,不该该使用 row-reverse,而是直接在文档源中去改变子项目的顺序。

Flexbox 的两条轴线

咱们已经讲解了 flexbox 的一个重要特性:可以将主轴(main axis)的方向从行切换为列。这种轴的方向切换,就是为何我经常认为网格布局中的对齐更容易理解的缘由。由于在网格布局中,在两个方向上你均可以采用几乎相同的方式来实现对齐。而对于弹性布局来讲会更麻烦点,由于在主轴(main axis)和交叉轴(cross axis)上,子项目的表现是不太相同的。

咱们已经了解了主轴(main axis),即你用 flex-direction 属性的值来定义的那根轴线。交叉轴(cross axis)则在另外一个方向上。若是你设置 flex-direction: row,那么你的主轴(main axis)是沿着行的方向,你的交叉轴(cross axis)是沿着列的方向。若是设置 flex-direction: column,主轴(main axis)是沿着列的方向,交叉轴(cross axis)是沿着行的方向。在这里咱们就须要讨论 flexbox 的另一个重要特色,那就是它与屏幕的物理方向无关。咱们不讨论从左到右方向的行,或从上到下方向的列,由于状况并不是老是如此。

书写模式

当我在上文中描述行和列的时候,我提到了块级和行内的维度 dimensions。这篇文章是用英文写的,它是水平的书写模式。这就意味着当你要 Flexbox 显示一行时,子项目会水平的展现。在这个例子中,main-start 位于左边 —— 也就是英文书写模式中中句子开始的位置。

若是我使用的是从右到左书写的语言,好比阿拉伯语的话,起始位置就会位于右边:

HTML:

<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
</div>
复制代码

CSS:

body {
  padding: 20px;
  font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}

* {box-sizing: border-box;}

p {
  margin: 0 0 1em 0;
}

.container {  
  border: 5px solid rgb(111,41,97);
  border-radius: .5em;
  padding: 10px;
  display: flex;
  flex-direction: row;
  direction: rtl;
}

.item {
  width: 100px;
  height: 100px;
  padding: 10px;
  background-color: rgba(111,41,97,.3);
  border: 2px solid rgba(111,41,97,.5);
}
复制代码

请看 Rachel Andrew(@rachelandrew)在 CodePen 上的这个例子:Smashing Flexbox Series 1: row with rtl text

flexbox 的初始值意味着,若是我所作的只是建立一个 flex 容器,个人子项目将会从右侧开始显示,而且向左排列。内联方向的起始位置是你正在使用的书写模式中句子开始的位置

若是你使用垂直书写模式而且使用的默认排列方向(这里指 flex-direction: row),此时的行就会是垂直方向的,由于这就是垂直书写方式语言排列行的方式。你能够尝试为 flex 容器设置 writing-mode 属性,把值设置为 vertical-lr。如今,你再把 flex-direction 设置为 row,子项目就会排成垂直的一列了。

HTML:

<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
</div>
复制代码

CSS:

body {
  padding: 20px;
  font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}

* {box-sizing: border-box;}

p {
  margin: 0 0 1em 0;
}

.container {  
  border: 5px solid rgb(111,41,97);
  border-radius: .5em;
  padding: 10px;
  display: flex;
  flex-direction: row;
  writing-mode: vertical-lr;
}

.item {
  width: 100px;
  height: 100px;
  padding: 10px;
  background-color: rgba(111,41,97,.3);
  border: 2px solid rgba(111,41,97,.5);
}
复制代码

请看 Rachel Andrew(@rachelandrew)在 CodePen 上的这个例子:Smashing Flexbox Series 1: row with a vertical writing mode

因此,一行能够水平的排列,main-start 位于左侧或者右侧,也能够垂直排列,main-start 位于顶部。即便咱们的思惟习惯了横向排列的文本,很难想象一行垂直排列的文本,但它的 flex-direction 属性的值仍然是 row

为了让子项目按照块级维度进行排列,咱们能够把 flex-direction 的值设置成 column 或者 column-reverse。在英语(或者阿拉伯语)这样的水平书写模式里,子项目会从容器顶部开始按照垂直方向排列。

在垂直书写模式中,块级方向横跨整个页面,这也是这种书写模式下块级元素的排列方向。若是你将一列设置为为 vertical-lr,那么这些块级元素会从左到右进行排列(内部的文本方向仍为垂直排列)。

HTML:

<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
</div>
复制代码

CSS:

body {
  padding: 20px;
  font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}

* {box-sizing: border-box;}

p {
  margin: 0 0 1em 0;
}

.container {  
  border: 5px solid rgb(111,41,97);
  border-radius: .5em;
  padding: 10px;
  display: flex;
  flex-direction: column;
  writing-mode: vertical-lr;
}

.item {
  width: 100px;
  height: 100px;
  padding: 10px;
  background-color: rgba(111,41,97,.3);
  border: 2px solid rgba(111,41,97,.5);
}
复制代码

请看 Rachel Andrew(@rachelandrew)在 CodePen 上的这个例子:Smashing Flexbox Series 1: column in vertical-lr writing mode

可是,不管块级元素怎么显示,只要你使用的是 column 方向,那么元素始终处在块级维度之中。

了解一行或者一列可以在不一样的物理方向上运行,有助于咱们理解网格布局和弹性布局中的一些术语。在网格布局和弹性布局中,并不会使用『左和右』、『上和下』这样的方向,由于咱们并不会指定文档的书写模式。如今全部的 CSS 都变的更注重书写模式了。若是你对其余已经支持这种方向差别的 CSS 属性和值有兴趣的话,能够读一下个人这篇文章 Logical Properties and Values

总结一下,记住:

  • flex-direction: row

    • 主轴 = 行内维度
    • main-start 位于当前书写模式下句子开头的位置
    • 交叉轴 = 块级维度
  • flex-direction: column

    • 主轴 = 块级维度
    • main-start 位于当前书写模式下块级元素的开头位置
    • 交叉轴 = 行内维度

默认对齐方式

当咱们设置 display: flex 时,还会发生一些事情,默认的对齐方式会发挥做用。在该系列的其余文章中,咱们会好好地了解一下对齐方式。可是,咱们如今在探索 display: flex 的时候,也应该看一下这些发挥做用的默认值。

注意值得注意的是,尽管这些对齐属性始于 Flexbox 规范,但 Box Alignment(盒模型对齐)会最终覆盖 Flexbox 规范的相关内容,如 flexbox 规范中所述。

主轴对齐方式

justify-content 属性的默认值是 flex-start,就像咱们的 CSS 写的那样:

.container {
    display: flex;
    justify-content: flex-start;
}
复制代码

这就是咱们的 flex 子项目(flex item)从 flex 容器的起始边缘开始排列的缘由。一样也是当咱们设置 row-reverse 时,它们变为从结束边缘开始排列的缘由,由于那个边缘变成了主轴(main axis)的起点。

当你看见对齐属性以 justify- 开头时,这个属性就是做用于主轴(main axis)的。也就是说 justify-content 属性规定主轴(main axis)的对齐方式,并将子项目从起始边缘对齐。

justify-content 还有其余的值:

  • flex-end
  • center
  • space-around
  • space-between
  • space-evenly(在块级对齐方式中添加)

这些值用来处理 flex 容器中剩余空间的分布。这就是子项目间会发生移动或者说相互分隔的缘由了。若是你添加属性 justify-content: space-between,剩余空间被平均分配给子项目。固然,这只有在容器有剩余空间的状况下才会发生。若是你的 flex 容器被所有充满了(子项目排列完后,没有任何剩余空间),那么设置 justify-content 属性不会产生任何效果。

你能够把 flex-direction 设置成 column。由于 flex 容器在没有高度的状况下不会有剩余空间,因此设置 justify-content: space-between 不会发生变化。若是你把容器设置成比展现所须要的高度更高的话,那么这个属性就会发挥做用了:

HTML:

<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
</div>
复制代码

CSS:

body {
  padding: 20px;
  font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}

* {box-sizing: border-box;}

p {
  margin: 0 0 1em 0;
}

.container {  
  border: 5px solid rgb(111,41,97);
  border-radius: .5em;
  padding: 10px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 500px;
}

.item {
  width: 100px;
  height: 100px;
  padding: 10px;
  background-color: rgba(111,41,97,.3);
  border: 2px solid rgba(111,41,97,.5);
}
复制代码

请看 Rachel Andrew(@rachelandrew)在 CodePen 上的这个例子:Smashing Flexbox Series 1: column with a height

交叉轴的对齐方式

子项目在单一交叉轴的 flex 容器中也会沿着这根交叉轴对齐。这里执行的对齐是子项目沿着交叉轴相互对齐。在接下来的这个例子中,有一个子项目比其余项占据更高的空间,而后其余的子项目会按照某种规范来拉伸到与它相同的高度,这个规范就是 align-items 属性,由于它的初始值就是 stretch

HTML:

<div class="container">
  <div class="item">One</div>
  <div class="item">Two</div>
  <div class="item">
    <ul>
    <li>Three: a</li>
    <li>Three: b</li>
    <li>Three: c</li>
    </ul>
    </div>
</div>
复制代码

CSS:

body {
  padding: 20px;
  font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}

* {box-sizing: border-box;}

p {
  margin: 0 0 1em 0;
}

.container {  
  border: 5px solid rgb(111,41,97);
  border-radius: .5em;
  padding: 10px;
  display: flex;
}

.item {
  padding: 10px;
  background-color: rgba(111,41,97,.3);
  border: 2px solid rgba(111,41,97,.5);
}

.item ul {
  margin: 0;
  padding: 0;
  list-style: none;
}
复制代码

请看 Rachel Andrew(@rachelandrew)在 CodePen 上的这个例子:Smashing Guide to Layout: clearfix

当你在 flex 布局中看到有一个属性是以 align- 开头的,那个就是交叉轴的对齐方式,align-items 属性规定子项目在沿着交叉轴方向上的对齐方式,这个属性的其余的值包括:

  • flex-start
  • flex-end
  • center
  • baseline

若是你不想其余的子项目跟拉伸到跟最高的那一项同样高的话,设置 align-items: flex-start,它会把子项目都沿着交叉轴的起始位置对齐。

HTML:

<div class="container">
  <div class="item">One</div>
  <div class="item">Two</div>
  <div class="item">
    <ul>
    <li>Three: a</li>
    <li>Three: b</li>
    <li>Three: c</li>
    </ul>
    </div>
</div>
复制代码

CSS:

body {
  padding: 20px;
  font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}

* {box-sizing: border-box;}

p {
  margin: 0 0 1em 0;
}

.container {  
  border: 5px solid rgb(111,41,97);
  border-radius: .5em;
  padding: 10px;
  display: flex;
  align-items: flex-start;
}

.item {
  padding: 10px;
  background-color: rgba(111,41,97,.3);
  border: 2px solid rgba(111,41,97,.5);
}

.item ul {
  margin: 0;
  padding: 0;
  list-style: none;
}
复制代码

请看 Rachel Andrew(@rachelandrew)在 CodePen 上的这个例子:Smashing Flexbox Series 1: align-items: flex-start

flex 子项目的初始值

终于说到这里了,flex 子项目(flex item)也是有初始值的,它们包括:

  • flex-grow: 0
  • flex-shrink: 1
  • flex-basis: auto

这意味咱们的子项目(flex item)在默认状况下不会自动充满主轴上的剩余空间。若是 flex-grow 被设置成一个正数,才会致使子项目拉伸并占据剩余空间。

这些子项目(flex item)一样能够收缩。默认状况下,flex-shrink 的值被设置成了 1。这就意味着,若是咱们的 flex 容器很是小,那么其中的子元素在溢出容器以前就会自动的缩小以适应容器大小。这是一个很是灵活的属性。总的来讲就是,子项目在容器没有足够空间去排列的状况下依然能保持在容器以内,而且不会溢出。

为了在默认状况下得到最好的展现效果,flex-basis 属性的默认值被设置成 auto,咱们会在这个系列的其余文章中好好了解这表明什么。如今,你只须要将 auto 理解为『大到足够适应容器』就好了。在这种状况下,当 flex 容器中有一些子项目,其中的一个子项目相较于其余包含更多的内容,那么它会被分配更多的空间。

HTML:

<div class="container">
  <div class="item">Two words</div>
  <div class="item">Now three words</div>
  <div class="item">
    This flex item has a lot of content and so it is going to need more space in the flex container.
    </div>
</div>
复制代码

CSS:

body {
  padding: 20px;
  font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}

* {box-sizing: border-box;}

p {
  margin: 0 0 1em 0;
}

.container {  
  border: 5px solid rgb(111,41,97);
  border-radius: .5em;
  padding: 10px;
  display: flex;
  width: 400px;
}

.item {
  padding: 10px;
  background-color: rgba(111,41,97,.3);
  border: 2px solid rgba(111,41,97,.5);
}
复制代码

请看 Rachel Andrew(@rachelandrew)在 CodePen 上的这个例子:Smashing Flexbox Series 1: initial values of flex items

上面这个例子就是弹性布局灵活性的体现。在 flex-basis 属性的默认值是 auto,而且子项目(flex item)没有设置尺寸的状况下,它就会有一个 max-content 的基础尺寸。这就是它根据内容自动延伸以后没有通过任何其余包装的尺寸。而后,子项目按照比例来占据剩余空间,详见如下 flexbox 规范中的说明。

『注意:分配收缩空间时,是根据基本尺寸乘以弹性收缩系数(flex shrink factor)。收缩空间的大小与子项目设置的 flex-shrink 的大小成比例。好比说有一个较小的子项目,在其余较大的子项目明显收缩以前,是不会被收缩到没有空间的。』

也就是说较大的子项目收缩的空间更多,那么如今咱们获得了最终的布局结果。你能够比较一下下面两个截图,都是使用上面的例子,不一样的是在第一个截图中,第三个盒子内容较少占据的空间较小,所以相对的每一列的占据的空间更均匀一些。

这是较大的子项目占据更多空间的例子

其余项为了给较大的一项提供空间而自动收缩(Large preview)。

弹性布局会试图帮助咱们得到一个合理的最终显示结果,而不须要写 CSS 的人来定义。它不会平均的减小每行的宽度,从而造成一个每行只有几个单词的很高的子项目,而是会给该子项目分配更多的空间用以展现其内容。这种表现正是如何正确弹性布局的关键。它最适用于用于沿着一条轴线排列的元素,以一种灵活和感知内容的方式。这里我简单介绍了一点细节,但在接下来的系列文章中咱们会更加深刻的了解这些算法。

总结

在这篇文章中,我用弹性布局的属性的一些默认值来介绍当你设置 display: flex 的时候,究竟发生了什么。使人惊讶的是,当你逐步分解以后,发现它原来有这么多内容,而且这些内容就包含了弹性布局的核心特色。

弹性布局是很是灵活的:它会根据你的内容自动地作出不错的选择 —— 经过收缩和拉伸达到最好的展现效果。弹性布局还能感知书写模式:布局中行和列的方向跟书写模式有关。弹性布局经过分配空间,容许子项目在主轴(main axis)上以总体的的方式来对齐。它还容许子项目按照交叉轴来对齐,使得交叉轴上的项目相互关联。更重要的是,弹性布局知道你的内容有多大,而且尽可能采用更好的方式来展现你的内容。在接下来的文章中,咱们会更加深刻的探索,思考咱们何时以及为何要用弹性布局。

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索