CSS3总结五:弹性盒子(flex)、弹性盒子布局

时间:2023-03-09 09:39:12
CSS3总结五:弹性盒子(flex)、弹性盒子布局
  • 弹性盒子容器的属性与应用

  • display:flex/inline-flex
  • flex-direction
  • flex-wrap
  • justify-content
  • align-items
  • align-content
  • flex-flow
  • 弹性盒子子项的属性与应用

  • order
  • align-self
  • flex
  • flex-grow
  • flex-shrink
  • flex-basis
  • 弹性盒子布局

  • flex居中/T字布局
  • 可动态增加导航栏
  • 等分布局
  • 圣杯布局
  • 流式布局

一、弹性盒子容器的属性与应用

1.1.定义弹性盒子display:flex/inline-flex

在盒模型(box)中剖析了单个元素的盒模型结构,那么display则是用来定义盒子在文档中的模型结构,比如display:inline表示为内联元素,display:block表示为块级元素,dispaly:inline-block表示为行级块元素;还可以设置dispaly:table表示为表格等,而这篇博客是来剖析CSS3的弹性盒子,flex表示为弹性块级盒子,inline-block表示为行级块盒子,这两者的区别与块级元素和行级块元素是一样的,就是于相邻元素的排列方式不同。

CSS3总结五:弹性盒子(flex)、弹性盒子布局 CSS3总结五:弹性盒子(flex)、弹性盒子布局

注:display在CSS属性中被分类为布局,与float、clear、visibility、overflow划分为同一类别的属性,但是在这个分类中都是首先基于display的展示行为为基础,才有浮动,清除浮动,是否可见,溢出处理等状态。所以display作为布局的核心功能,除了定义外部的元素与元素之间的排列方式,从CSS2到CSS3更是通过扩展display的参数由原来内部固定的结构模型扩展到了可自定义的结构模型,就是弹性盒子flex。

在弹性盒子中,原来的margin,pading,content还有width的内部计算方式都不在基于浏览器内核默认的模型来计算,而是通过与flex相关的一系列属性来定义计算盒子内部项(内部元素)的排列布局。这部分CSS内容对应着浏览器内核渲染页面时内部调用的layout()方法。可以这么来说,原来的layout()直接通过识别块级元素、行级块元素、行级元素、表格这四种固定实现的布局模型直接调用固定的计算模型计算出元素在页面中展示的(结构)样式,而CSS3中的flex布局模式则需要浏览器内核调用自定义的flex相关参数来展示元素的(结构)样式。【以上这部分内容需要对浏览器渲染原理有一定的了解,详细可以参考:浏览器加载解析渲染网页原理

CSS3总结五:弹性盒子(flex)、弹性盒子布局

1.2.以主轴和交叉轴为布局逻辑基础的子项位置布局

1.2.1通过flex-direction定义主轴以及主轴方向,row(横向自左向右)、row-reverse(横向自右向左)、column(纵向自上向下)、column-reverse(纵向自下向上)。交叉轴始终与主轴成相对状态。

最常见的主轴与交叉轴的切换应用就是导航栏在屏幕足够宽的时候呈横排展示,当浏览器窗口缩小到一定程度时,导航栏呈纵向排列展示。

CSS3总结五:弹性盒子(flex)、弹性盒子布局

下面再来看一些具体的flex-direction的示例:

a
b
c
a
b
c
a
b
c
a
b
c

实现以上示例的核心代码:

flex-direction:row;/*demo1--该值为默认值*/
flex-direction:column;/*demo2--*/
flex-direction:row-reverse;/*demo3--*/
flex-direction:column-reverse;/*demo4--*/

1.2.2通过flex-wrap定义单行或多行的弹性盒子布局,nowrap(单行布局,默认值)、wrap(多行布局)、wrap-reverse(多行,且按每行反转排列)。该属性主要用于启用弹性盒子的多行布局功能,而这里的多行并非传统的纵横排列上的行,准确的来说是当主轴上方向上出现压缩时在交叉轴上开启多个平行主轴方向的布局。看下列示例:

a
b
c
d
e
f
a
b
c
d
e
f
a
b
c
d
e
f
a
b
c
d
e
f
a
b
c
d
e
f

实现以上示例的核心代码:

 /*demo1--按照默认的主轴方向和默认的单行布局*/
flex-direction:row;
flex-wrap:nowrap;
/*demo2--主轴方向为纵向,默认单行布局*/
flex-direction:column;
flex-wrap:nowrap;
/*demo3--主轴方向默认,采用多行布局*/
flex-direction:row;
flex-wrap:wrap;
/*demo4--主轴方向默认,采用多行且反向排列布局*/
flex-direction:row;
flex-wrap:wrap-reverse;
/*demo5--主轴方向为纵轴,采用多行布局*/
flex:direction:column;
flex-wrap:wrap;

关于flex布局的多行与单行可以用块级元素和行级块元素的排列布局来理解,并且在多行布局也就真正意义上实现了交叉轴的的意义,wrap-reverse可以理解为交叉轴方向上的反向布局,所以它只会影响到行的起始位置和排列,并不会影响到每行子项的排列。

1.2.3弹性盒子flex的对齐属性:justify-content、align-items、align-content。

justify-content:表示主轴方向的对齐属性,对齐方式分别有轴的正方向(起始方向)对齐、轴的反方向对其、轴的居中对齐、项目均衡散列对齐(最前方和最后方的项目与父级没有间距)、项目正反两侧等距对齐(类似两侧加上margin)===【flex-start、flex-end、center、space-between、space-around】;

align-items:表示交叉轴方向的(行内轴)对齐属性,属性值(同解析中的类别顺序),对齐方式分别有轴的正方向对齐,轴的反方向对齐、基于行内轴的中心对齐、基于内容底线对齐(也可以说是基于内容对齐)、将项基于交叉轴方向基于父级最大访问伸缩===【flex-start、flex-end、center、baseline、strecth】;

align-content表示所有项目的整体对齐方式(只对多行起作用,准确的说这个属性才是交叉轴的对齐方式)对齐方式分别有轴的正方向对齐、轴的反方向对其、轴的居中对齐、沿交叉轴的行列均衡散列对齐、沿交叉轴的行列两侧等距对齐、沿交叉轴的行列平均分配空间的伸缩===【flex-start、flex-end、center、space-between、space-around、strecth】

a
b
c
a
b
c
a
b
c
a
b
c
a
b
c

以上五个示例是justify-content的五个属性值所表现的效果,代码:

 justify-content:start;
justify-content:end;
justify-content:center;
justify-content:space-between;
justify-content:space-around;
a
b
c
a
b
c
a
b
c
a
b
c
a
b
c

以上五个示例是align-items的五个属性值所表现的效果,代码:

 align-items:flex-start;/*所有子项沿上边缘对齐,并紧贴父级的上内边缘*/
align-items:flex-end;/*所有子项沿下边缘对齐,并紧贴父级的下内边缘*/
align-items:center;/*所有子项沿行内轴中线对齐*/
align-items:baseline;/*这里子项b添加了margin-top:15px;子项c添加了padding-top:15px;*/
align-items:stretch;/*这里所有子项没有设置height*/
a
b
c
d
e
a
b
c
d
e
a
b
c
d
e
a
b
c
d
e
f
g
a
b
c
d
e
f
g
a
b
c
d
e

以上六个示例是align-content的五个属性值所表现的效果,代码:

 flex-wrap:wrap;/*所有align-content示例必须都是在多行列(相对主轴)的情况下才生效,所以这行代码是所有示例中都有*/
align-content:flex-start;/*示例一*/
align-content:flex-end;/*示例二*/
align-content:center;/*示例三*/
align-content:space-between;/*示例四*/
align-content:space-around;/*示例五*/
align-content:stretch;/示例六,所有子项不设置height/

1.2.4最后弹性盒子容器上还有一个属性就是flex-flow,这是个符合属性,相当于flex-flow :<flex-direction> || <flex-wrap>;符合属性的应用就不多解释了,但是由于弹性盒子的功能复杂性建议还是按照拆分形式书写代码,也有利于代码的可读性(语义化)。

二、弹性盒子子项的属性与应用

2.1order子项排序,所有子项order默认值为0,用来指定子项的排列位置;

a
b
c
a
b
c
d
e
f

上面示例中对应的order序号:

示例一:a:2,b:1,c:0;

示例二:a:5,b:4,c:3,d:2,e:1,f:0;

2.2align-self单个子项沿着交叉轴方向定位,在flex-wrap:wrap的多行多列情况下不起作用,align-self的权重大于align-items。

a
b
c
d
e
f

以上示例的关键代码:

 /*父级容器的css代码*/
display:flex;
justify-content: space-between;
align-items:center;
/*各个子项的align-self*/
align-self:auto;/*继承align-items的属性*/
align-self:flex-start;/*交叉轴的起始端*/
align-self:flex-end;/*交叉轴的末尾端*/
align-self:center;/*交叉轴的中间*/
align-self:baseline;/*表示行内轴的最上方,与flex-start的效果一致*/
align-self:stretch;/*基于交叉轴方向伸缩,不设置交叉轴方向的宽高*/

注:align-items和align-self只能作用于flex-wrap:nowrap(默认值)的弹性盒子上。

2.3复合属性flex:none | <' flex-grow '> <' flex-shrink >'? || <' flex-basis '>。用来设置弹性盒子子项的伸缩比率,flex-grow表示伸展比率、flex-shrink表示缩小比率、flex-basis表示弹性盒子子项在主轴方向上的最小宽度或高度。具体的计算公式通过实例来逐一解析:

2.3.1伸展比率flex-grow的计算公式与示例【重点】:

公式:if(弹性盒子内容宽度 > 子项宽度之和 )==>flex-grow生效;

拉伸计算:(弹性盒子内容宽度 - 子项宽度之和)/ 子项flex-grow之和 * 每个子项的flex-grow比率 = 每个子项的拉升宽度

解说:flex-grow默认值为0,拉伸计算是按照拉伸比率分配剩下的空间。拉升方向是基于主轴方向向主轴的两侧拉伸。

CSS3总结五:弹性盒子(flex)、弹性盒子布局

示例代码:

 <div class="wrapper">
<div class="content">1</div>
<div class="content">2</div>
<div class="content">3</div>
</div>
<!--css-->
*{
margin: 0;
padding: 0;
}
.wrapper{
width: 600px;
height: 600px;
border: 1px solid black;
display: flex;
flex-direction: row;
}
.wrapper:hover{
flex-direction: column;
} .content{
height: 100px;
box-sizing: border-box;
} .content:first-of-type{
width: 100px;
background-color: #aff;
flex-grow: 1;
}
.content:nth-of-type(2){
width: 80px;
background-color: #0ff;
flex-grow: 3;
}
.content:nth-of-type(3){
width: 50px;
background-color: #ffa;
}

2.3.2缩小比率flex-shrink的计算公式与示例【重点】:

公式:if(弹性盒子内容宽度 < 子项宽度之和 )==>flex-shrink生效;

缩小计算:(当前被计算的子项宽度 * 当前被计算的子项的flex-shrink /(每个子项宽度 * 该子项的flex-shrink + 每个子项宽度 * 该子项的flex-shrink + ...))* ( 所有子项宽度之和 - 父级弹性盒子的内容宽度)== 当前被计算的子项要缩小的宽度;

解说:flex-shrink的默认值是1,实际上flex-shrink的缩小计算需要进行加权平均。如果设置flex-shrink:0表示该子项不参与压缩。

CSS3总结五:弹性盒子(flex)、弹性盒子布局

示例代码:

 <div class="wrapper">
<div class="content">1</div>
<div class="content">2</div>
<div class="content">3</div>
</div>
<!--css-->
*{
margin: 0;
padding: 0;
}
.wrapper{
width: 600px;
height: 300px;
border: 1px solid black;
display: flex;
flex-direction: row;
}
.content{
height: 100px;
} .content:first-of-type{
width: 200px;
background-color: #aff;
}
.content:nth-of-type(2){
width: 200px;
background-color: #0ff;
}
.content:nth-of-type(3){
width: 400px;
background-color: #ffa;
flex-shrink: 3;
}

关于flex-shrink子项缩小的计算除了加权平均进行缩减以外,还需要注意的是,在实际的加权平均计算中,并非使用子项的width计算,而实质上是使用真实的内容区宽度content-width来进行计算的。这里特别需要主义的就算是子项使用box-sizing:border-box;其flex-shrink的计算中也不会包含border和padding的宽度(标准盒模型下也是使用content-width计算)。示例:

CSS3总结五:弹性盒子(flex)、弹性盒子布局

示例代码:

 <div class="wrapper">
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
</div>
<!--css-->
*{
margin: 0;
padding: 0;
}
.wrapper{
width: 600px;
height: 300px;
border: 1px solid black;
display: flex;
flex-direction: row;
}
.content{
width: 200px;
height: 100px;
box-sizing: border-box;
} .content:first-of-type{
padding: 0 100px;
background-color: #aff;
}
.content:nth-of-type(2){
padding: 0 100px;
background-color: #0ff;
}
.content:nth-of-type(3){
width: 400px;
background-color: #ffa;
flex-shrink: 3;
}

关于flex-shrink加权平均的计算标的为content-width的示例二:

CSS3总结五:弹性盒子(flex)、弹性盒子布局
 CSS3总结五:弹性盒子(flex)、弹性盒子布局

示例代码:

 <div class="wrapper">
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
</div>
<!--css-->
*{
margin: 0;
padding: 0;
}
.wrapper{
width: 600px;
height: 300px;
border: 1px solid black;
display: flex;
flex-direction: row;
}
.content{
width: 200px;
height: 100px;
box-sizing: border-box;
padding: 0 80px;
} .content:first-of-type{
background-color: #aff;
}
.content:nth-of-type(2){
background-color: #0ff;
}
.content:nth-of-type(3){
width: 400px;
background-color: #ffa;
flex-shrink: 3;
}

2.3.3弹性盒子子项在主轴方向上的最小宽高flex-basis。

CSS3总结五:弹性盒子(flex)、弹性盒子布局

示例代码:

 <div class="wrapper">
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
</div>
<!--css-->
*{
margin: 0;
padding: 0;
}
.wrapper{
width: 600px;
height: 600px;
border: 1px solid black;
display: flex;
flex-direction: row;
}
.wrapper:hover{
flex-direction: column;
}
.content{
flex-basis: 200px;
width: 100px;
height: 100px;
box-sizing: border-box;
padding: 0 80px;
}
.content:nth-of-type(1){
background-color: #aff;
}
.content:nth-of-type(2){
background-color: #ffa;
}
.content:nth-of-type(3){
background-color: #0ff;
}

通过上面的示例了解到了flex-basis的作用是在主轴方向上的,然后还有就是它并不能完全替代width或者height,因为flex-basis表示的是子项的主轴方向上的宽高的最小值,所以可以同时设置flex-basis和width、height;如果只设置flex-basis不设置width、height的话,子项可以被内容无限撑开至弹性盒子父级容器的宽度,如果设置width、heigth的话则至撑开至width、height设置的宽高。

但是如果设置的flex-basis的值大于width、height的话,则width、height的值会被覆盖。(这里需要特别注意英文的换行,最好设置英文单词可以截断)

三、弹性盒子布局

在具体介绍弹性盒子布局之前,先说明一下这里的示例不考虑(旧)弹性盒子的兼容性问题,部分浏览器并未全部兼容新的标准弹性盒子,safari浏览器就是最好的例子。然后这里只介绍flex盒子的布局模式,后期会有一篇全面解读页面布局的博客。

3.1应用flex实现居中布局与T字布局

实现flex居中布局和T字布局的完全代码:

 /*
*.wrapper表示父级
*.content表示子项
* 只保留关键代码,具体代码可以通过控制台查看示例代码
*/
/*示例一*/
.wrapper{
display: flex;
justify-content: center;
align-items: center;
}
.content{
flex: 0 0 50%;
height: 50%;
}
/*示例二:效果与示例一一致*/
.wrapper{
display:flex;
justify-content:center;
}
.content{
flex: 0 0 50%;
align-self: center;
}
/*示例三*/
.wrapper{
width: 500px;
height: 300px;
position: absolute;
top: calc(50% - 150px);
left: calc(50% - 250px);
border: 1px solid #aee;
border-radius: 10px; display: flex;
flex-wrap: wrap;
} .content{
flex: 0 0 50%;
background-color: #7CFC00;
}
.content:first-of-type{
border-top-left-radius:10px;
}
.content:nth-of-type(2){
border-top-right-radius:10px;
background-color: #6A5ACD;
}
.content:last-of-type{
flex-basis: 100%;
border-radius:0px 0px 10px 10px;
background-color: #FF7F00;
}
/*示例四*/
.wrapper{
width: 500px;
height: 300px;
position: absolute;
top: calc(50% - 150px);
left: calc(50% - 250px);
border: 1px solid #aee;
border-radius: 10px; display: flex;
flex-wrap: wrap;
} .content{
flex: 0 0 50%;
background-color: #7CFC00;
}
.content:first-of-type{
flex-basis: 100%;
border-radius:10px 10px 0px 0px;
}
.content:nth-of-type(2){
border-bottom-left-radius:10px;
background-color: #6A5ACD;
}
.content:last-of-type{
border-bottom-right-radius:10px;
background-color: #FF7F00;
}
/*示例五*/
.wrapper{
width: 500px;
height: 300px;
position: absolute;
top: calc(50% - 150px);
left: calc(50% - 250px);
border: 1px solid #aee;
border-radius: 10px; display: flex;
flex-wrap: wrap;
} .content{
background-color: #7CFC00;
}
.content:first-of-type{
flex-basis: 100%;
border-radius:10px 10px 0px 0px;
}
.content:nth-of-type(2){
flex: 0 0 30%;
border-bottom-left-radius:10px;
background-color: #6A5ACD;
}
.content:nth-of-type(3){
flex: 0 0 35%;
border-bottom-left-radius:10px;
background-color: #660066;
}
.content:last-of-type{
flex: 0 0 35%;
border-bottom-right-radius:10px;
background-color: #FF7F00;
}

3.2可动态增加标签导航栏

HTML5
CSS3
ES6
VUE
...

实现可动态增加标签导航栏示例的代码:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>可动态增加的导航栏</title>
<link rel="stylesheet" href="">
<style>
*{
margin: 0;
padding: 0;
}
.wrapper{
position: absolute;
top: calc(50% - 15px);
left: calc(50% - 250px); width: 500px;
height: 30px;
border:1px solid #2b2b2b ;
box-sizing: border-box; display: flex;
}
.content{
flex: 1 1 auto; text-align: center;
line-height: 30px;
font-size: 20px;
}
.content:hover{
background-color: #2b2b2b;
color: #fff;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="content">HTML</div>
<div class="content">CSS</div>
<div class="content">ES6</div>
<div class="content">SPA</div>
<div class="content">...</div>
<!--可新增以下标签-->
<!-- <div class="content">VUE</div>
<div class="content">node</div>
<div class="content">小程序</div> -->
</div>
</body>
</html>

3.3等分布局(中间固定宽度,两边自适应)

left
middle
right

示例代码:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>等分布局</title>
<!--中间宽度固定,两边自适应-->
<link rel="stylesheet" href="">
<style>
*{
margin: 0;
padding: 0;
}
.wrapper{
resize: both;
overflow: hidden; width: 600px;
height: 400px;
border: 1px solid #2b2b2b; display: flex;
}
.content{
box-sizing: border-box;
}
.content:first-of-type{
flex: 1 1 auto;
}
.content:nth-of-type(2){
flex: 0 0 400px;
border-left: 1px solid #2b2b2b;
border-right: 1px solid #2b2b2b;
}
.content:last-of-type{
flex: 1 1 auto;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
</div>
</body>
</html>

 3.4使用flex实现圣杯布局

圣杯布局的要求:

☯全局纵向分三部分:头部、尾部高度固定,中间自动;宽度都为100%;

☯中间被拆分为三栏:左右宽度固定,中间自适应;

header
left
center
right
footer

示例代码:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>圣杯布局</title>
<!-- 圣杯布局的要求
-- 纵向分为上中下三部分,上中下宽度100%,上下高度固定;中间部分高度自动。
-- 中间被拆分为三栏:左右宽度固定,中间自适应;
-->
<link rel="stylesheet" href="">
<style>
*{
margin: 0;
padding: 0;
}
:root,
body{
width: 100%;
height: 100%;
}
.wrapper{
width: 100%;
height:100%; display: flex;
flex-direction: column;
}
.content{
width: 100%;
box-sizing: border-box;
border: 1px solid #2b2b2b;
}
.content:first-of-type{
flex: 0 0 70px;
}
.content:nth-of-type(2){
flex: 1 1 auto;
display: flex;
}
.content:last-of-type{
flex: 0 0 100px;
}
.content:nth-of-type(2)>div{
box-sizing: border-box;
border: 1px solid #2b2b2b;
}
.left, .right{
flex: 0 0 150px;
}
.middle{
flex: 1 1 auto;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="content"></div>
<div class="content">
<div class="left"></div>
<div class="middle"></div>
<div class="right"></div>
</div>
<div class="content"></div>
</div>
</body>
</html>

3.5使用flex实现流式布局

示例代码:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>flex实现流式布局</title>
<link rel="stylesheet" href="">
<style>
*{
padding: 0;
margin: 0;
}
:root,
body{
width: 100%;
height: 100%;
background-color: #2b2b2b;
}
.wrapper{
position: absolute;
top: calc(50% - 200px);
left: calc(50% - 300px);
width: 600px;
height: 500px;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.item{
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="item" style="background-color: #663300;"></div>
<div class="item" style="background-color: #663333;"></div>
<div class="item" style="background-color: #663366;"></div>
<div class="item" style="background-color: #333300;"></div>
<div class="item" style="background-color: #333333;"></div>
<div class="item" style="background-color: #333366;"></div>
<div class="item" style="background-color: #FF8C00;"></div>
<div class="item" style="background-color: #FF83FA;"></div>
<div class="item" style="background-color: #FF8247;"></div>
<div class="item" style="background-color: #FF3030;"></div>
<div class="item" style="background-color: #FF1493;"></div>
<div class="item" style="background-color: #FF0000;"></div>
<div class="item" style="background-color: #BF3EFF;"></div>
<div class="item" style="background-color: #B22222;"></div>
<div class="item" style="background-color: #ADFF2F;"></div>
<div class="item" style="background-color: #8B8B00;"></div>
<div class="item" style="background-color: #8B864E;"></div>
<div class="item" style="background-color: #663300;"></div>
<div class="item" style="background-color: #663333;"></div>
<div class="item" style="background-color: #663366;"></div>
<div class="item" style="background-color: #333300;"></div>
<div class="item" style="background-color: #333333;"></div>
<div class="item" style="background-color: #333366;"></div>
<div class="item" style="background-color: #FF8C00;"></div>
</div>
</body>
</html>