那些年Flex弹性布局中你所迷惑的点

前言闲话:javascript

很久很久,没有写博文了,此处批评下懒惰的本身,哈哈~。为啥忽然写这篇文章呢,是由于前几天我刷题,正好刷到flex相关求长度的题目,结果算错了,虽然平时写了不少布局页面,可是感受本身这块仍是理解不够深入,后面看了不少不一样人的题目解析,本身也从中有了一些收获心得,必要总结一下吧,自我学习,若有幸帮助到其余人,我也会很开心。css

关于Flex弹性布局迷惑点,我暂时从两大点进行阐述html

迷惑1:flex属性简写迷惑

平常写页面布局的时候,对于Flex布局,咱们通常都会用flex属性来简写其弹性盒子项(flex item)的三个属性:java

  • flex-grow
  • flex-shrink
  • flex-basis

下面我列出了常见的几类简写形式,其中我以为特别容易记错的就是单值简写,例如flex: 1flex: autoflex: none表明什么?这类的问题…反正我以前有时候老记错他们的默认值,汗-_-||web

/*单值简写*/
 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,表示若是存在剩余空间,也不放大。
注意 不容许为负值svg

flex-shrink 属性表示弹性盒子项(flex item)的收缩因子,即缩小比例,默认为1,表示若是空间不足,该项目将缩小。
注意 不容许为负值布局

flex-basis 属性表示弹性盒子项(flex item)在主轴方向上的初始大小或者叫原本大小(官方术语:分配多余空间以前,项目占据的主轴空间)。根据这个属性,计算主轴是否有多余空间,它的默认值为auto
注意 不容许为负值学习

再补充一句:flex-basis 的值能够是数字带绝对单位例如 px; 也能够是一个百分数,百分数是相对于其父弹性盒容器的宽或者高(取决于主轴的方向)计算。若是不使用 box-sizing 来改变盒模型的话,那么这个属性就决定了 flex 元素的内容盒(content-box)的宽或者高(取决于主轴的方向,即父元素的flex-direction的设置)的尺寸大小。flex

具体详细关于Flex布局的介绍语法,温习或者学习请移步戳大牛文章,阮一峰: Flex 布局教程:语法篇.文章很详细,反正我以前忘记了就去看一眼,哈哈

迷惑2:flex属性计算迷惑

其实概念可能你们都懂,可是碰到相关的计算时,就容易发蒙。了解其真正的计算规则和方法,才能让咱们真正的理解和更好的使用,下面我就以题目的形式,分析解决一些关于flex属性的计算迷惑

举例:两列左右布局,如left和right

<div class="container">
  <div class="left"></div>
  <div class="right"></div>
</div>

问题:求 leftright 的实际宽度?

状况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点:

  1. 只要父元素设置了Flex布局,而且子元素(弹性盒子item)设置了flex属性,那item的的长或宽就会受其影响
  2. flex-basis只要为0%,那item设置了长度也无效
  3. 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的值),以及知道空间究竟是剩余(放大)仍是溢出(收缩),按照上面的思路,无论几列均可以算清楚,从而驾轻就熟理解透彻,也有助于写页面时更好的把控页面布局。

嗯,大概本身的总结就先到这吧,后续迷惑点有了再写喽…😁