「译」Flexbox 基本原理

11

Flexbox

整理自 MDN web docs 的笔记,同时参考了 Web Bos 上的什么是 Flexbox系列视频。

介绍

Flexbox 是 Flexible Box Module 的缩写。 它是一种布局模型,允许我们方便地控制 html 元素之间的空间分布和对齐 [2]。

Flexbox 一次只能控制一个维度的定位(行或者列)。二维定位的控制需要依靠网格布局 [2]。

给出以下模板:

<body>
    <div class="container">
    <div class="box box-1">1</div>
    <div class="box box-2">2</div>
    <div class="box box-3">3</div>
    <div class="box box-4">4</div>
    <div class="box box-5">5</div>
    <div class="box box-6">6</div>
    <div class="box box-7">7</div>
    <div class="box box-8">8</div>
    <div class="box box-9">9</div>
    <div class="box box-10">10</div>
    </div>
</body>

上面 div 的行为默认遵循正常的 html 文档流,因此从上到下、从左到右进行渲染,并且会占据整个 body 的宽度,因为它们的 display 属性默认是 block

Normal document flow

弹性项目

当为 .containerdiv 设置 display: flex 时,所有的直接子 div 将成为弹性项目,并且获得新的行为 [2]:

  • 由于 flex-direction 默认值为 row,因此它们会呈一行排列
  • 它们将会从左到右排列

Display flex

  • 项目不会依靠伸展来适应整个宽度(主轴),相反,它们采用收缩的方式

Items shrink to fit

  • 项目将会伸展以适应交叉轴(在这个例子中是高度)。如果各个项目的高度不同,它们将会伸展至与最高的那个项目等高。
  • flex-basis 默认值为 auto(项目宽度将取决于其自身的内容)
  • flex-wrap 默认值为 nowrap(如果容器的宽度不足以囊括所有的项目,则项目不会换行,只会溢出)

出于可视化的目的,我们拉伸容器以占据整个高度。

弹性容器

display: flex 使容器拓展整个可用宽度;与之相对的,display: inline-flex 使容器宽度塌陷至与内容宽度相等。

Display inline flex

弹性方向

一旦声明为弹性容器,我们就可以将元素看作位于两条轴中。一条是由 flex-direction 定义的主轴,一条是与前者垂直的交叉轴 [2]。

flex-direction 属性有四个值: row, row-reverse, columncolumn-reverse.

默认值是 row,它将主轴设置为从左到右的水平方向,而交叉轴从上到下与之垂直相交。同理,column 将主轴设置为从上到下的垂直方向,而交叉轴则是从左到右。对这两个值添加 reverse ,则主轴将反转 180°,而交叉轴保持不变 1。

可以通过下图观察这些值对应的弹性项目行为:

Flex direction

弹性换行

当容器空间不足以容纳全部弹性项目时,利用 flex-wrap 属性处理弹性项目 [3]。

flex-wrap 的默认值为 nowrap,这意味着如果容器不能在保留项目原始宽度的同时将它们排列成一行的话,项目将会收缩以进行适应。如果由于某些原因无法收缩,则项目会溢出容器外 1。

通过给项目设置 300px 的宽度,nowrap 选项输出下面这个结果:

flex-wrap: nowrap

其中,每个项目收缩到大约 70px 以适应容器。

当属性值更新为 wrap 时,项目的宽度将等于原先的值,300px。当第一行的宽度不足以容纳 300px 时,项目不再溢出容器外,而是会换行 [3]。每一行都应该被视为是一个独立的弹性容器,任何一个容器内的空间分布均不会影响与之相邻的其他容器 [2]。

flex-wrap: wrap

但是为什么弹性项目会占据整个屏幕的高度呢?在第一部分,容器高度被设置为 100vh,因此可用空间被这四行平分以适应 300px 的项目。假如我们没有设置 100vh,则容器高度将等于项目内容的高度,如下图所示 [1]:

Wrap/height unset

另一个选项是 wrap-reverse,它将会反转交叉轴。通过属性 flex-direction 设置的从上到下的方向会被 wrap-reverse 转化为从下到上 [1]。

flex-wrap: wrap-reverse

通过 flex-direction: column 反转主轴,容纳不下的元素将会换行至另一列,同时剩余空间会被平分 [1]。

flex-wrap/column

wrap-reverse 选项与 column 方向搭配使用,则将反转交叉轴的方向为从右到左,产生如下输出:

flex-wrap/column

弹性布局是一维布局,虽然在反转换行的时候,项目会从下到上排列(在方向为 row 的情况下),但是依然保持着从左到右的结构。改变的只有交叉轴。

弹性流

flex-directionflex-wrap 可以在一个单属性中进行声明: flex-flow: [direction] [wrap] [2]。

.flex-container {
    flex-flow : column wrap;
}

项目之间的空隙

回到主轴方向为 row 且进行换行的情况。通过给项目设置 width: 33.3333%,容器能够完全被填满。

item width: 33.33%

但是当你让子 div 之间有空隙时,它们将不会像预期的那样进行换行:

Broken gutter

可以通过使用 CSS 函数 calc() 解决这个问题 [1]:

.flex-item {
    width: calc(33.33333% - 40px);
    margin: 20px;
}

Gutter

为了消除容器边缘的空间,这里对容器设置负外边距 [3]:

Gutter no edge

.flex-container {
    margin: -20px;
}

顺序

order 属性允许修改项目的呈现顺序。顺序是以组为单位进行分配的。默认情况下所有的弹性项目都设置为 order:0 ,这意味着所有的项目位于同一组,并且它们会按照原始顺序进行定位。如果有两个或者两个以上的组,那么各组将会相对于它们的整数值进行排序 [4]。

在下面的例子中,有三个顺序组-101,它们按照如下顺序排列。

.box-3 { order:  1; }
.box-7 { order:  1; }
.box-8 { order: -1; }

Order

表面上,这个属性重新分配了项目,但在诸如使用 tab 键对它们进行遍历的交互中则依然保留它们的原始位置。如果项目顺序与可访问性有关的话,这一点是需要考虑的。同理, flex-direction 也是这样 [4]。

Order and Accessibility

对齐

Alignment Digest

在弹性布局中,沿着轴的项目对齐和空间分布可以通过四个属性控制 [5]:

  • justify-content:将所有项目在主轴上对齐
  • align-items :将所有项目在交叉轴上对齐
  • align-self:将单个项目在主轴上对齐
  • align-content:控制交叉轴上各条线之间的空间

justify-content

justify-content digest

justify-content 是一个在主轴上处理项目的容器属性。最常用的 6 个值是: flex-startflex-endcenterspace-aroundspace-betweenspace-evenly。其中,默认值为 flex-start

align-items

align-items digest

align-items 同样是一个容器属性,它在交叉轴上处理项目的对齐。 默认值是 stretch ,其他值是 flex-startflex-endcenterbaseline [5]。

如果设置了容器高度,则 stretch 属性值会使所有的项目伸展至与容器等高;如果没有设置,则所有项目与最高的项目等高 [5]。上面第一张图片中容器高度设置为 100vh,第二张图片则没有设置高度。

align-content

align-content digest

align-content 是第四个也是最后一个容器属性,它在交叉轴上分配各条线之间的空间。作为最后一个属性,它的初始值为 stretch ,并且和 justify-content 一样接受以下几个属性值:flex-startflex-endcenterspace-aroundspace-betweenspace-evenly [5]。

align-self

align-self digest

align-items 属性实际上是通过给容器内的所有项目设置 align-self 而生效的。通过单独设置 align-self,可以覆盖先前设置的总的属性值。该属性和 align-items 拥有相同的可选值,但是还多了一个 ‘auto’ [5]。

auto 会重置 align-self 的值,使之重新等于通过 align-items 给容器全局定义的值 [5]。

弹性项目大小

项目的大小和弹性可以通过三个属性控制:flex-growflex-shrinkflex-basis。这些属性都在主轴上发挥作用 [2]。

  • flex-grow:如果有额外空间,每个项目应该如何伸展
  • flex-shrink:如果空间不足,每个项目应该如何收缩
  • flex-basis:在设置以上两个属性之前项目的大小

flex-grow

该属性设置的是弹性增长系数,这是一个用于处理项目之间相对大小的比率 [7]。

默认值是 0,这意味着如果有剩余空间,就把这个空间放在最后一个项目的后面 [1]。

flex-grow-default

在上面的例子中,direction 设置为 row,每个弹性项目的宽度是 60px。由于容器的宽度是 980px ,因此有 680px 的可用空间,这个空间称为 正向自由空间 [7]。

通过将 flex-grow 设置为 1,正向自由空间将会被弹性项目平分。每个项目的宽度都会增加 136px,总的宽度是 196px [7]。

flex-grow: 1

通过给第三个项目设置 flex-grow: 2 ,它获得的可用正向自由空间是其他项目的两倍,即比起其他项目的 173px ,它的总宽度为 286px [7]。

下图中,项目的 flex-grow 属性设置为自身的内容值。

flex-grow: variable

flex-shrink

当容器没有足够空间来容纳所有项目时,使用 flex-shrink 处理项目大小。因此,它通过收缩项目来处理它们的负向自由空间 [7]。

如下图所示,980px 的容器存放着 5 个 300px 宽度的容器。由于没有足够空间来容纳所需要的 1500px,默认的弹性收缩系数 1 会使每个项目收缩至 196px

flex-shrink-default

通过给第三个项目设置 2 的比率,它会比其它项目小两倍。

flex-shrink-1

下图中,每个项目以自身内容值作为弹性收缩比率。

flex-shrink-variable

flex-basis

flex-basis 属性会在实际设置可用空间之前检查每个项目应该具有的大小。默认值是 auto,项目宽度要么通过 width 显式设置,要么等于内容宽度。它同样也接受像素值 [7]。

下面的 gif 展示了一个 800px 宽度的容器和 5 个设置了 flex-basis: 160px 的弹性项目。这告诉浏览器:理想情况下有足够的空间放置所有的项目,项目的 160px 宽度将会得到保留,并且没有正向/负向自由空间。如果没有足够的空间,由于 flex-shrink 默认为 1,所有的项目会均匀地收缩。如果有剩余的空间,由于 flex-grow 默认为 0,剩余空间会放置在最后一个项目的后面。

flex-basis

下面的 gif 中,项目 1 设置为 flex-shrink: 10,项目 4 设置为 flex-grow: 10。对于负向自由空间,项目 1 减小的宽度是其它项目减小宽度的 10 倍;对于正向自由空间,项目 4 增加的宽度是其它项目增加宽度的 10 倍。

flex-basis

flex-basis 还可以接受值 content。此时,无论有没有设置 width,自由空间计算都只会基于项目内容去计算宽度。如果你不打算在计算时考虑项目宽度,则将其设置为 0

flex

flexflex-growflex-shrinkflex-basis 的简写属性 [2]。

它接受下面的预定于值:

  • initial:重置为弹性布局的默认值,与 flex: 0 1 auto 效果一样
  • auto:弹性项目可以根据需要伸展/收缩,与 flex: 1 1 auto 效果一样
  • none:使项目失去弹性,与 flex: 0 0 auto 效果一样
  • flex: 1:弹性项目可以伸展/收缩,并且 flex-basis 设置为 0 ,与 flex: 1 1 0 效果一样

Autoprefixer

考虑到跨浏览器兼容性,给属性加上所有必要的前缀很重要,这可以确保提供全面的支持 [1]。

手动给每个属性添加前缀是一项非常繁琐的任务,并且还会徒增样式维护的难度。作为替代方法,Gulp 可以自动化地完成这些任务。

要使用 Gulp,我们需要将其作为依赖项添加到项目中。这是通过 package.json 文件完成的,它负责跟踪依赖项及其版本。通过终端创建文件类型 [1]:

    🌹  npm init

在提示下输入信息,点击回车键进行确认。输出文件大概类似下面这样:


{
    "name": "project-name",
    "version": "1.0.0",
    "description": "Project description",
    "main": "index.js",
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "Author Name",
    "license": "ISC"
}
    

全局安装 gulp :

    🌹  npm install gulp -g

安装 gulp 和 gulp-autoprefixer 作为项目的依赖性:

    🌹  npm install gulp --save-dev
    🌹  npm install gulp-autoprefixer --save-dev

它们会出现在 package.json 文件的 devDependencies 键下。

创建一个 gulpfile.js 文件:

    🌹  touch gulpfile.js

添加下面内容 [9]:

//gulpfile.js

var gulp = require('gulp');
var autoprefixer = require('gulp-autoprefixer');

gulp.task('styles', function() {
    return gulp.src('./styles.css')
        .pipe(autoprefixer({ browsers: ['last 2 versions'], cascade: false }))
        .pipe(gulp.dest('build'));
});

gulpstyles.css 中提取内容并通过 gulp-autoprefixer 进行传递。输出文件保留在 build 文件夹下。

引用

译者注:
弹性项目:Flex Items
弹性容器:Flex Container
弹性方向:Flex Direction
弹性换行:Flex Wrap
弹性流:Flex Flow
弹性项目大小:Flexbox Sizing

你可能感兴趣的

liusy · 6月10日

老哥,好多图片都打不开哎...

回复

0

已修复

Chor 作者 · 6月11日
lancetian · 7月30日

这,能叫原理吗?

回复

载入中...