那些年Flex弹性布局中你所迷惑的点
前言闲话:
好久好久,没有写博文了,此处批评下懒惰的自己,哈哈~。为啥突然写这篇文章呢,是因为前几天我刷题,正好刷到flex相关求长度的题目,结果算错了,虽然平时写了很多布局页面,但是感觉自己这块还是理解不够深刻,后面看了很多不同人的题目解析,自己也从中有了一些收获心得,必要总结一下吧,自我学习,如有幸帮助到其他人,我也会很开心。
关于Flex弹性布局迷惑点,我暂时从两大点进行阐述
迷惑1:flex属性简写迷惑
日常写页面布局的时候,对于Flex布局,我们一般都会用flex属性来简写其弹性盒子项(flex item)的三个属性:
- flex-grow
- flex-shrink
- flex-basis
下面我列出了常见的几类简写形式,其中我觉得特别容易记错的就是单值简写,例如flex: 1或flex: auto或flex: none代表什么?这类的问题…反正我之前有时候老记错他们的默认值,汗-_-||
/*单值简写*/
flex: none; /* flex: 0 0 auto */
flex: 1; /* flex: 1 1 0% */
flex: 2; /* flex: 2 1 0% */
flex: 100px; /* flex: 1 1 100px */
flex: 50%; /* flex: 1 1 50% */
flex: auto; /* flex: 1 1 auto */
/*双值简写*/
flex: 1 2; /* flex: 1 2 0% */
flex: 1 100px; /* flex: 1 1 100px */
flex: 1 50%; /* flex: 1 1 50% */
flex: 1 auto; /* flex: 1 1 auto */
/*三值简写*/
flex: 2 1 auto; /* flex: 1 1 0% */
flex: 2 1 0%; /* flex: 2 1 0% */
flex: 2 1 100px; /* flex: 2 1 100px */
嗯,看完了是吗?那我们简单的说一下这三个属性吧(提倡简写,所以请大家熟记默认值)
flex-grow 属性表示弹性盒子项(flex item)的拉伸因子,即放大比例,默认值为0,表示如果存在剩余空间,也不放大。
注意 不允许为负值
flex-shrink 属性表示弹性盒子项(flex item)的收缩因子,即缩小比例,默认为1,表示如果空间不足,该项目将缩小。
注意 不允许为负值
flex-basis 属性表示弹性盒子项(flex item)在主轴方向上的初始大小或者叫本来大小(官方术语:分配多余空间之前,项目占据的主轴空间)。根据这个属性,计算主轴是否有多余空间,它的默认值为auto。
注意 不允许为负值
再补充一句:flex-basis 的值可以是数字带绝对单位例如 px; 也可以是一个百分数,百分数是相对于其父弹性盒容器的宽或者高(取决于主轴的方向)计算。如果不使用 box-sizing 来改变盒模型的话,那么这个属性就决定了 flex 元素的内容盒(content-box)的宽或者高(取决于主轴的方向,即父元素的flex-direction的设置)的尺寸大小。
具体详细关于Flex布局的介绍语法,温习或者学习请移步戳大牛文章,阮一峰: Flex 布局教程:语法篇.文章很详细,反正我之前忘记了就去看一眼,哈哈
迷惑2:flex属性计算迷惑
其实概念可能大家都懂,但是碰到相关的计算时,就容易发蒙。了解其真正的计算规则和方法,才能让我们真正的理解和更好的使用,下面我就以题目的形式,分析解决一些关于flex属性的计算迷惑
举例:两列左右布局,如left和right
<div class="container">
<div class="left"></div>
<div class="right"></div>
</div>
问题:求 left 和 right 的实际宽度?
情况1(存在剩余空间,item项目放大)
* {
padding: 0;
margin: 0;
}
.container {
width: 600px;
height: 50px;
display: flex;
}
.left {
flex: 1; /* 1 1 0% */
background: red;
}
.right {
flex: 2; /* 2 1 0% */
background: blue;
}
这个比较简单,大多数人都可以很快做出来,因为left占1份,right占2份,所以如下计算:
// 父容器 width: 600px
let widthLeft = 600px * 1/3 = 200px;
let widthRight = 600px * 2/3 = 400px;
但是,如果稍微改一下题目,更改left如下
left {
width: 300px
flex: 1; /* 1 1 0% */
background: red;
}
/*再或者这样*/
left {
width: 300px
flex: auto; /* 1 1 0% */
background: red;
}
那结果是什么呢?widthLeft是多少?widthRight 是多少?如果此刻的你依然可以准确快速算出,那恭喜你,说明你已经真正理解flex其中的计算,但如果有小伙伴会感到一点点蒙,那建议你耐心往下看。
(题外话:当然实践是检验真理的最好方式,放到浏览器里跑一下,大家就知道结果了,但是如果这是在笔试做题或者无电脑的环境中呢?如何准确作答?)
先分析第一题:
left只设置 flex: 1;
left设置flex:1,即flex: 1 1 0%; right设置flex:2,即flex: 2 1 0%,之前理解的总空间里left占1份,right占2份,没有问题,但是flex-basis: 0%这个表示弹性盒子项item初始大小为 0% * 600px = 0px,即初始大小为0,又因为left的flex-grow:1,right的flex-grow:2,那left和right的实际宽度就是初始长度 + 放大长度
// 父容器 width: 600px
let basisWid = 0;
let leftGrowWid = 600px * 1/3 = 200px; // left的放大比例长度
let rightGrowWid = 600px * 2/3 = 400px; // right的放大比例长度
let widthLeft = basicWid + leftGrowWid = 200px; // left实际长度
let widthRight = basicWid + rightGrowWid = 400px; // right实际长度
题目变形1:
left设置 width: 300px;
其实,这里的width:300px是一个干扰条件,用来专门迷惑你的,它有和没有结果都是一样的。计算的思路还是和上面一样,答案不变。
题目变形2:
left设置 width: 300px;
left更改为 flex: auto;
现在情况不一样了,left的flex属性变为了auto,即flex: 1 1 auto, flex-basis为auto的表示会按照left的设置宽度进行计算,因为left的宽度现在为300px,那么left就要按照当前300px的基础上进行放大,所以:
// 父容器 width: 600px
let leftBasisWid = 300px; // left初始因为为flex-basis为auto,所以取决于left的width
let rightBasisWid = 0; // right初始大小依然为0
let leftSpace = 600px - 300px = 300px; // 剩余空间大小
let leftGrowWid = leftSpace * 1/3 = 100px; // left的放大比例长度
let rightGrowWid = leftSpace * 2/3 = 200px; // right的放大比例长度
let widthLeft = leftBasisWid + leftGrowWid = 400px; // left实际长度
let widthRight = rightBasisWid + rightGrowWid = 200px; // right实际长度
这里你需要记住3点:
- 只要父元素设置了Flex布局,并且子元素(弹性盒子item)设置了flex属性,那item的的长或宽就会受其影响
- flex-basis只要为0%,那item设置了长度也无效
- flex-basis只要为auto,那item设置了长度就会作为它的初始长度
情况2(剩余空间不足,item项目缩小)
* {
padding: 0;
margin: 0;
}
.container {
width: 600px;
height: 50px;
display: flex;
}
.left {
flex: 1 2 500px;
background: red;
}
.right {
flex: 2 1 400px;
background: blue;
}
这个就是另一种情况了,情况1的学习中大家应该知道如何判断,空间到底是富裕还是不足了吧。这题可以明显可以看出父容器的空间不足(left和right的初始宽度之和大于600),那这时候,对于left和right的实际长度就是flex-shrink在参与计算,发挥作用。
可能有些小伙伴会这样分析计算:
1.left容器 放大比例1,缩小比例 2 默认占500px
2.right容器 放大比例2,缩小比例 1 默认占400px
当子项目宽度总和大于父容器宽度时,如果有缩小比例将按照缩小比例进行压缩,比如子项目总和900px大于父容器600px,多出的300px将按照缩小比例进行压缩
left容器将会压缩2/3* 300 = 200px
right容器将会压缩1/3* 300 =100px
所以最终left和right宽度都是300px
同理当子项目宽度总和小于父容器宽度时,如果有放大比例将按照放大比例进行扩张。
但是实际把代码放到浏览器运行下,发现结果并不是200和100,而是left:285.72px,right:314.28px
注意 弹性盒子item的缩小计算比放大计算要稍微复杂一些,但是搞清楚后,一样信手拈来。上面提到的“同理当子项目宽度总和小于父容器宽度时,如果有放大比例将按照放大比例进行扩张。”这个是对的,但是子项目宽度总和大于父容器宽度时,计算不能想上面那样,是错误的,毕竟浏览器是不会骗你的。
解析:首先,left和right的flex-shrink分别为2和1,flex-basis分别为500px和400px,则我们可以知道(500 + 400 > 600),剩余空间不足,项目会溢出300,但是因为此时left和right都会收缩,所以收缩值需要先计算,最终长度应该等于:item初始长度 - 收缩长度
// 父容器 width: 600px,left初始长度500px,right初始长度为400px
let leftBasisWid = 500px; // left初始因为为flex-basis为500px
let rightBasisWid = 400px; // right初始因为为flex-basis为400px
let overSpace = (500px + 400px) - 600px = 300px; // 剩余空间不足,溢出空间为300px
let totalWeight = 2 * 500 + 1 * 400 = 1400; // 总权重值(2和1为left和right的flex-shrink值)
let leftRatio = 2 * 500 / totalWeight; // left的收缩比
let rightRatio = 1 * 400 / totalWeight; // right的收缩比
let leftShrinkWid = overSpace * leftRatio = 214.28px; // left的缩小比例长度
let rightShrinkWid = overSpace * rightRatio = 85.72px; // right的缩小比例长度
let widthLeft = leftBasisWid - leftShrinkWid = 285.72px; // left实际长度
let widthRight = rightBasisWid - rightShrinkWid = 314.28px; // right实际长度
举例:三列左右布局,如left middle right
上题基础上多增加一个middle,left和right样式还保持不变
<div class="container">
<div class="left"></div>
<div class="middle"></div>
<div class="right"></div>
</div>
.middle {
width: 200px;
flex: 0 1 20%;
background: yellow;
}
问题:求left middle right的实际长度是多少?
解析:middle的flex属性设置为0 1 20%,之前两列情况最后有总结到,flex-basis有值的时候,设置了width也只是干扰条件不参与进来,middle初始长度就是父元素长度600 * 20% = 120px,,完整计算如下:
// 父容器 width: 600px,left初始长度500px,right初始长度为400px
let leftBasisWid = 500px; // left初始因为为flex-basis为500px
let rightBasisWid = 400px; // right初始因为为flex-basis为400px
let middleBasisWid = 120px; // middle初始因为为flex-basis为20%,所以为600px * 20% = 120px
let overSpace = (500px + 400px + 120px) - 600px = 420px; // 剩余空间不足,溢出空间为420px
let totalWeight = 2 * 500 + 1 * 400 + 1 * 120 = 1520; // 总权重值(2 1 1为left right middle的flex-shrink值)
let leftRatio = 2 * 500 / totalWeight; // left的收缩比
let rightRatio = 1 * 400 / totalWeight; // right的收缩比
let middleRatio = 1 * 120 / totalWeight; // middle的收缩比
let leftShrinkWid = overSpace * leftRatio = 276.31px; // left的缩小比例长度
let rightShrinkWid = overSpace * rightRatio = 110.52px; // right的缩小比例长度
let middleShrinkWid = overSpace * middleRatio = 33.16px; // middle的缩小比例长度
let widthLeft = leftBasisWid - leftShrinkWid = 223.96px; // left实际长度
let widthRight = rightBasisWid - rightShrinkWid = 289.48px; // right实际长度
let widthMiddle = middleBasisWid - middleShrinkWid = 86.84px; // middle实际长度
结语
本文主要从题目场景入手,进行解析,让大家动手中理解。题目变化当然是灵活的,但其实你只需要搞清楚flex属性设置的值的意思(尤其是flex-basis的值),以及知道空间到底是剩余(放大)还是溢出(收缩),按照上面的思路,不管几列都可以算清楚,从而得心应手理解透彻,也有助于写页面时更好的把控页面布局。
嗯,大概自己的总结就先到这吧,后续迷惑点有了再写喽…😁