前段时间接手了一个基于cef1的项目,由于其特别限定的场景,在查询了对html5的支持后,整站都采用了flexbox布局,也从头熟悉了一遍该布局的使用方法。故分享出来,供参考。
背景
Flexbox Layout
,俗称Flexible box模型,由W3C于2009年开始起草的css3布局样式。它旨在提供一种更加有效的布局方式,控制父容器中子元素的布局,排列以及分布,甚至在它们的尺寸未知或动态变化的情况下,都能够做到正确的展现(弹性盒子中的flex
也由此而得名)。
弹性盒子的核心概念是父容器拥有能够改变其子元素的的宽度/高度和排列顺序,使得子元素能够以最佳的尺寸填充整个父容器的可用空间。简单来说,一个弹性盒子能够充分扩展它的子元素尺寸使其填满自身的可用空间,或者收缩子元素来防止溢出。
最重要的一点是,相对于传统的块布局block
以及行布局inline
来说,弹性盒子模型是方向不可知的(direction-agnostic
)。尽管块布局以及行布局能够很好的满足页面布局,但是它们缺乏弹性,不能很好地支持大型或者是复杂的应用(特别在屏幕进行横竖屏切换,改变视图尺寸,延伸,收缩等等复杂情景下)。
注意:弹性盒子布局适合作用在一个应用的组件和小范围的布局,例如,一个歌曲列表,一个导航条,等等。相对的,Grid layout,即栅格布局则倾向于进行大规模的界面布局,例如,整体界面的分栏布局,左右结构,上下结构,等等。
基本原理
由于弹性盒子是一整套模型而不是单独的一个css属性,它包含了一个属性集合,其中的一些属性作用于父容器(flex container
),另一些则作用于子元素(flex items
),所以特别需要区分这些属性的作用对象。
如果说常规布局是建立在块级和行级方向(block and inline flow directions
)上的,那么弹性盒子布局则是建立弹性流方向(flex-flow directions
)上的。
如上图所示,假设主轴是横向的,那么子元素将会沿着主轴从左至右依次排列,或者是沿着与主轴垂直的交叉轴由上至下依次排列。下面我们一一进行剖析:
main axis(主轴)
父容器的主轴是子元素排列的基本轴,但这并不意味着基本轴必须是横向的,这取决于父容器的flex-direction
属性(后边会介绍到)。例如,如果子元素是竖向排列的,那么主轴则是竖向的那条轴。
main-start | main-end
子元素会沿着main-start从左至右排列,直到main-end。值得注意的是,默认情况下,子元素只会排列在一排上,就算已经到达了父容器的右边缘,也不会进行换行,除非设置了flex-wrap
属性(后边会介绍到)。
main size
在父容器中主轴上的子元素的主尺寸(不论宽度或者高度)之和,构成了弹性盒子的主尺寸。例如,如果子元素是横向排列的,则宽度则是主尺寸,相对的,如果子元素是竖向排列的,则高度则是主尺寸。
cross axis
垂直于主轴的轴,称之为交叉轴。显然,交叉轴的方向取决于主轴的方向。
cross-start | cross-end
当父容器中的子元素换行时,子元素的行排列的方向则是沿着交叉轴进行的,从cross-start开始,直到cross-end。
cross size
和main size同理,只是方向与之垂直。
下面进行弹性盒子属性详解,分为两类,分别是父容器属性和子元素属性。
父容器属性(flex-container)
display
用于定义弹性盒子的显示方式。
.flex-container {
display: flex; // 实际相当于block-flex,块级容器,宽度同其外层容器
display: inline-flex; // 顾名思义,行级容器,宽度取决于其子元素
}
flex-direction
用于定义主轴方向,同时也决定了子元素的排列方向。
.flex-container {
flex-direction: row; // 子元素由左至右排列(默认值)
flex-direction: row-reverse; // 子元素由右向左排列
flex-direction: column; // 子元素由上至下排列
flex-direction: column-reverse; // 子元素由下至上排列
}
flex-wrap
默认情况下,所有的子元素都会尝试沿着主轴在排列在同一行(列)上,这个属性用来对子元素进行换行排列,即当子元素排列到main-end的时候,会自动进行换行。
.flex-container {
flex-wrap: nowrap; // 子元素都在排列在同一行(默认值)
flex-wrap: wrap; // 子元素将沿着交叉轴正向排列在多行中
flex-wrap: wrap-reverse; // 子元素将沿着交叉轴反向排列在多行中
}
flex-flow
flex-direction和flex-wrap的属性缩写。
.flex-container {
flex-flow: <'flex-direction'> || <'flex-wrap'>;
flex-flow: row nowrap; // 横向排列 不换行(默认值)
}
justify-content
定义子元素在主轴上的对其方式。主要用在当所有的子元素在同一行,且为非弹性元素时,分配剩下的额外空间;或者是弹性元素但是并没有撑满整个父容器的主尺寸。该属性也能够对溢出的子元素起到一定的控制作用,例如,当子元素溢出时,对其进行居中,则左右溢出的宽度将是相等的。
.flex-container {
justify-content: flex-start; // 子元素向主轴起点看齐排列(默认值)
justify-content: flex-end; // 子元素向主轴终点看齐排列
justify-content: center; // 子元素居中排列
justify-content: space-between; // 子元素以相同的间距从主轴的起点和终点开始平均排列
justify-content: space-around; // 子元素以相同的边距延主轴平均排列
}
小贴士:
justify-centent: center;
还可以配合align-self: center;
进行内容的居中垂直布局。
align-items
用于定义排列在同一主轴的子元素在交叉轴方向上的排列方式(可以想象成是justify-content属性的交叉轴版)。
.flex-container {
align-items: flex-start; // 子元素向交叉轴起点看齐排列
align-items: flex-end; // 子元素向交叉轴终点看齐排列
align-items: flex-center; // 子元素在交叉轴居中排列
align-items: flex-stretch; // 子元素沿交叉轴拉伸排列(撑满整个交叉轴的长度)(默认值)
align-items: flex-baseline; // 子元素在交叉轴上沿其文本的基线对其排列
}
align-content
用于定义父容器中的多行/列在交叉轴上的排列方式(有点类似于多个子元素在主轴上的justify-content排列)。
.flex-container {
align-content: flex-start; // 多排子元素从交叉轴起点进行排列
align-content: flex-end; // 多排子元素从交叉轴终点进行排列
align-content: center; // 多排子元素在交叉轴居中排列
align-content: stretch; // 多排子元素沿交叉轴拉伸排列(撑满整个交叉轴的长度)(默认值)
align-content: space-between; // 多排子元素以相同的间距从交叉轴的起点和终点开始平均排列
align-around: // 多排子元素以相同的边距延交叉轴平均排列
}
小贴士:当只有一排子元素时,
align-content
属性并没有什么卵用。
子元素属性(flex-items)
order
默认情况下,子元素按照它们在源码中出现的位置进行排列。幸运的是,通过order属性,则能够控制子元素在父容器中的排列顺序,这大大增加了布局的灵活性。
上图中的数字,代表了元素的顺序值(整型,且接受负值),且order: 0;
与无order属性
等效。
.flex-item: {
order: <integer>;
order: -1|1|2...n;
}
flex-grow
用于定义子元素能否伸展的能力。它接受一个数字来作为比例值,这使得子元素能够自动检测父容器中的可用空间,并将其进行填充。
如果所有的子元素都有flex-grow: 1,那么所有子元素都将均分主轴的长度;如果其中有一个子元素有flex-grow: 2,那么这个子元素将占据其他flex-grow: 1的子元素占据主轴长度的两倍(至少会尽量这样做)。
.flex-item {
flex-grow: <number>; // 非负自然数(包括0)
flex-grow: 0; // 默认值
flex-grow: 1|2...n;
}
小贴士1:如果子元素的内容超过了其
flex-grow
所分配的空间,则会继续伸展,直到满足子元素内容的长度。
小贴士2:如果父容器设置了flex-wrap: wrap;
,那么挤到第二排的子元素将按照第二排的主轴长度进行flex-grow
比例的重新计算。
flex-shrink
用于定义了子元素收缩的能力。
上图中,父容器宽度固定为500px
,子元素设置了flex-basis: 120px; flex-shrink: 1;
,此时,D和E设置了flex-shrink: 2
。这样,D和E将会收缩自身的长度,尽量将所有的元素都排在父容器的主轴上且尽量不超过父容器的宽度500px
。
flex-basis
用于在空间被分配前,定义子元素的默认长度。这个值可以是长度(百分比,rem等等)或者是关键字(如auto等)。
.flex-item {
flex-basis: <length> | auto(默认值);
}
需要注意的是,如果flex-basis: 0;
,那么子元素内容旁边的额外空间是不计算在flex-basis
内的。如果设置为auto
,那么额外的空间将基于子元素的flex-grow
进行计算。计算方式如下图所示。
在上图中,子元素的flex-grow
为1:1:2
。当flex-basis: 0;
时,子元素内容旁边的额外空间不计数;flex-basis: auto;
时,子元素内容旁边的额外空间计数,且比例由其flex-grow
决定,这里则是1:1:2
。故而,虽然我们的flex-grow
设置为了1:1:2
,但是由于flex-basis
的影响,产生了不同的显示结果。
flex
flex-grow,flex-shrink,flex-basis的属性缩写。其中,flex-shrink和flex-basis是可选项,如不填写这两个值,则默认值为flex-shrink: 1,flex-basis: 0%。如果整个flex属性都不填写,则整个属性默认值为0 1 auto。
.flex-item {
flex: none | [ <'flex-grow'> <'flex-shrink'> || <'flex-basis'> ];
flex: 0 1 auto; // 不伸展 收缩度为1 自动基础长度
}
小贴士:强烈建议采用属性缩写的方式来定义子元素的弹性属性,因为它能帮助我们自动地设置许多默认值。
align-self
允许单个子元素覆写父容器的align-items属性(故两者拥有同样的属性值)。
.flex-item {
align-seft: auto | flex-start | flex-end | center | baseline | stretch;
}
小贴士:
float
,clear
,vertical-align
这些属性对一个弹性盒子布局的元素将失去作用。
欢迎交流,完。
TODO:Grid layout 栅格布局
参考文献
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
https://www.w3.org/TR/css-flexbox-1/