对于 flex-shrink 我们都知道它在 flex 布局中控制 flex 盒子空间不足时子元素改如何收缩,平常开发中更多的是使用默认值 1 或者设置 0。
那设置其他值的时候会有什么效果呢,不少文章中描述都不是很细,在很长一段时间我甚至以为自己是了解它的。

开篇我们带着几个问题
1. “flex-shrink 属性定义了项目的缩小比例,当父元素主轴方向空间不足的时候,子元素们按照 flex-shrink 的比例来缩小。” 这句描述对吗?
2. 一个父元素下有两个子元素,两个子元素各占用父元素 50% 且分别有 50px、20px 的 padding。这个很简单的需求用 flex 布局如何实现?如果尝试以后和你的想象不同,那为什么会这样呢?
3. 当空间不足时,各项目具体会缩小多少?子元素 `flex-shrink` 不同时有何影响?子元素宽度会对缩小有影响吗?父子元素的 margin、padding、border 会对结果有影响吗?box-sizing 的值会有影响吗?

如果你对以上的问题不能清楚的回答,或者尝试以后发现和自己想象的不同,那这篇文章对于你可能会有一些用。

首先我们看第一个问题
> 1. “flex-shrink 属性定义了项目的缩小比例,当父元素主轴方向空间不足的时候,子元素们按照 flex-shrink 的比例来缩小。” 这句描述对吗?
这句话描述其实不准确。
flex-shrink 决定了子元素缩小系数,但在具体计算的时候,其实它还受到了 flex base size 的影响。
w3c 对于的 flex-shrink 的描述有这样一段备注
> Note: The flex shrink factor is multiplied by the flex base size when distributing negative space. This distributes negative space in proportion to how much the item is able to shrink, so that e.g. a small item won’t shrink to zero before a larger item has been noticeably reduced.

从中我们可以看到,真正使用的缩小系数其实是 flex shrink factor * flex base size。下面我们用一个例子来说明它

<style>
.box {
display: flex;
width: 400px;
outline: 1px red solid; } .item1 {
flex: 0 2 300px;
background-color: #32d6d6;
} .item2 {
flex: 0 1 200px;
background-color: #e2a83e;
} .item3 {
flex: 0 2 100px;
background-color: #b85ad0;
}
</style>
...
<div class="box">
<div class="item1">1</div>
<div class="item2">2</div>
<div class="item3">3</div>
</div>

按照不准确的描述 `flex-shrink` 决定了子元素缩小系数,那我们知道子元素需要的空间是 300+200+100 一共 600px,但父元素只有 400px
所以分别的负空间是 200px,或者说需要缩小 200px。三个元素 `flex-shrink` 分别为 2 1 2,表面上看应该分别缩小 80 40 80,那三个元素应该 220 160 20。但事实是这样吗?
如果你也尝试一下,就会知道,实际上的效果是 180 160 60。
我们来看一下正确的计算方式:

flex-shrink * flex-base(姑且先这么写,之后会修正) => factor
2 * 300 => 600
1 * 200 => 200
2 * 100 => 200

所以三个元素真正的系数分别是 600/1000 200/1000 200/1000。200 的总额得出 120 40 40。可以看到和实际情况相符。

按照这个公式可以满足多数情况的使用,但其中还隐藏着其他规则。下面我们看第二个问题
>2. 一个父元素下有两个子元素,两个子元素各占用父元素 50% 且分别有 50px、20px 的 padding。这个很简单的需求用 flex 布局如何实现?如果尝试以后和你的想象不同,那为什么会这样呢?

该问题其实是我发现自己对 `flex-shrink` 不够了解,从而研究的原因。
这个问题看起来很简单吧,估计多数人第一反应是这样:

<style>
.box {
display: flex;
width: 400px;
outline: 1px red solid;
}
.item-2-1 {
flex: 1 1;
padding: 50px;
background-color: #32d6d6;
background-clip: content-box;
}
.item-2-2 {
flex: 1 1;
padding: 20px;
background-color: #b85ad0;
background-clip: content-box;
}
</style>
...
<div class="box">
<div class="item-2-1">1</div>
<div class="item-2-2">2</div>
</div>

看起来收缩、放大系数都相等,两个元素应该父元素 400 像素,每个 200 对吧?我们看一下实际情况


是不是和想象不同?

下面说明原因
w3c 里对于元素可用长度有这样的描述
>dimension of the flex container’s content box is a definite size, use that; if that dimension of the flex container is being sized under a min or max-content constraint, the available space in that dimension is that constraint; otherwise, subtract the flex container’s margin, border, and padding from the space available to the flex container in that dimension and use that value. This might result in an infinite value.

我们可用看到其实计算主轴可以用长度的计算是要去除 margin, border, and padding 的,但这里的描述我觉得其实也不准确,他这里说的只是 flex container,似乎只是父元素里的 margin、border、padding。但在我的实际测试的时候,其实还包括更多
比如:直接子元素的 margin、border、padding 甚至是直接子元素的 min-width,稍缓我会说为什么

那么我们来计算一下
400 - 50*2 - 20*2 = 260(可用空间)
flex-base 为 0,flex-grow 都为 1;260*(1/2)= 130
130 + 20 + 20 = 170

那么我们要如何实现子元素含 padding 时也平分空间呢?
flex-base 的描述里有这样的一句
> As another corollary, flex-basis determines the size of the content box, unless otherwise specified such as by box-sizing [CSS3UI].

可以看到 flex-basis 的数值设置的 width 其实是 box-sizing 的默认值 box-sizing: content-box;
那么理所当然会想到修改参数,改为 box-sizing: border-box; 然后 flex-base: 100%;
如此一来,两个元素都如 IE 盒模型一样,宽度包含 border padding,而且收缩、放大系数都一样,是不是就可以实现需求了呢?答案还是否定的,不但实现不了需求,甚至会出现一时间难以理解的数值

上面提到子元素的 padding 等值也会算在不可伸缩长度里冻结掉。为什么这么说呢,我们结合上图的原因来做解说

这个值是怎么来的呢,其经过了以下的步骤
1. 计算子元素 flex-base 所代表的实际值 => 400px
2. 那两个就是 800px,父元素 400px,主轴长度不够,flex-shrink 开始生效。但第二步却不是 flex-shrink * flex-base 得出真正的比例系数,我们需要先得到“真正的” flex base size,其实之前我们提及过
 事实上,真正的 flex base size 并非单纯的是 flex-base。更准确的说,子元素 flex-base 设置后带来的 content width。比如这里 flex base size = box width 400 - padding 20*2 - border 0 = 360,以及两外一个 400-50*2=300。
3. 计算比例系数 360*1=360,300*1=300。所有其比例系数分别是 300/660、360/660。
4. 计算需要分配的负空间 400*2-400 = 400px
5. 计算分别需要缩减的部分 400*(300/660)≈181.8181、400*(360/660)≈218.1818
6. 实际宽度 400-181.8181≈218.18 400-21≈181.81

对于其原因,w3c 里对于如何计算弹性长度有这样的一个描述 
> Size inflexible items. Freeze, setting its target main size to its hypothetical main size…

对此我是这样理解的,在计算子元素主轴长度的时候,有这么一些操作
把 flex container 的 margin、border、padding 所占的长度冻结,因为这些不可分配
把 子元素的 margin、border、padding 所占的空间冻结,因为这部分不会参与伸缩
剩下的空间才会作为正、负长度分配给子元素

所以我们得出更详细的压缩计算公式

flex_container_available_length = flex_container_content_width(or height)
flex_items_length = flex_item_box_width + flex_item_box_width + flex_item_box_width...
shrink_factor = (flex-shrink * flex_base_size) /((flex-shrink * flex_base_size) + (flex-shrink * flex_base_size) + ...)
will_allocate_length = flex_container - flex_items_length
flex_item_width = flex_item_box_width + flex_item_box_width * (will_allocate_length * shrink_factor)

注1:flex_item_box_width = margin + padding + border + content width
注2: flex_base_size 取决于该元素的 box-sizing 和 flex-base,其值为 border-box 时,flex_base_size = flex-base - padding - barder;其值为 content-box 时,flex_base_size = flex-base。

为了验证公式的正确性,我们随意设计一个 margin padding border box-sizing flex-shrink flex-base 多样繁杂的 demo(.flexBox)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>flex-shrink demo</title>
<style>
.box {
display: flex;
width: 400px;
outline: 1px red solid;
} .item1 {
flex: 0 2 300px;
background-color: #32d6d6;
} .item2 {
flex: 0 1 200px;
background-color: #e2a83e;
} .item3 {
flex: 0 2 100px;
background-color: #b85ad0;
} .item-2-1 {
flex: 1 1;
padding: 50px;
background-color: #32d6d6;
background-clip: content-box;
}
.item-2-2 {
flex: 1 1;
padding: 20px;
background-color: #b85ad0;
background-clip: content-box;
} .demo-3 {
flex: 1 1;
display: flex;
align-items: center;
}
.demo-3-1 {
flex: 1 1;
padding: 50px;
background-color: #32d6d6;
background-clip: content-box;
}
.demo-3-2 {
flex: 1 1;
padding: 20px;
background-color: #b85ad0;
background-clip: content-box;
} .item-border-box {
flex-basis: 100%;
box-sizing: border-box;
} .flexBox {
display: flex;
width: 1500px;
outline: 1px red solid;
}
.flexItem-1, .flexItem-2, .flexItem-3 {
flex-shrink: 2;
flex-basis: 300px;
}
.flexItem-1 {
margin: 0 10px;
}
.flexItem-2 {
padding: 0 20px;
border: 5px #ccc solid;
box-sizing: content-box;
}
.flexItem-3 {
padding: 0 20px;
border: 5px #ccc solid;
box-sizing: border-box;
}
.flexItem-4, .flexItem-5, .flexItem-6 {
flex-shrink: 1;
flex-basis: 200px;
}
.flexItem-4 {
padding: 0 10px;
}
.flexItem-5 {
border: 5px #ccc solid;
margin: 0 10px;
box-sizing: content-box;
}
.flexItem-6 {
border: 5px #ccc solid;
margin: 0 10px;
box-sizing: border-box;
}
.flexItem-7, .flexItem-8, .flexItem-9 {
flex-shrink: 2;
flex-basis: 100px;
}
.flexItem-7 {
border: 5px #ccc solid;
}
.flexItem-8 {
padding: 0 30px;
margin: 0 10px;
box-sizing: content-box;
}
.flexItem-9 {
padding: 0 30px;
margin: 0 10px;
box-sizing: border-box;
}
</style>
</head> <body>
<div class="box">
<div class="item1">1</div>
<div class="item2">2</div>
<div class="item3">3</div>
</div>
<br /> <div class="box">
<div class="item-2-1">1</div>
<div class="item-2-2">2</div>
</div>
<br /> <div class="box">
<div class="demo-3">
<div class="demo-3-1">
1
</div>
</div>
<div class="demo-3">
<div class="demo-3-2">
2
</div>
</div>
</div>
<br /> <div class="box">
<div class="item-2-1 item-border-box">1</div>
<div class="item-2-2 item-border-box">2</div>
</div>
<br /> <div class="flexBox">
<div class="flexItem-1" title="300 - (550*600/2770) ≈ 180.866">1</div>
<div class="flexItem-2" title="300 - (550*600/2770) ≈ 180.866">2</div>
<div class="flexItem-3" title="300 - (550*500/2770) ≈ 200.722 - 20*2 - 10*2 = 150.722">3</div>
<div class="flexItem-4" title="200 - (550*200/2770) ≈ 160.288">4</div>
<div class="flexItem-5" title="200 - (550*200/2770) ≈ 160.288">5</div>
<div class="flexItem-6" title="200 - (550*190/2770) ≈ 162.274 - 5*2 = 152.274">6</div>
<div class="flexItem-7" title="100 - (550*200/2770) ≈ 60.288">7</div>
<div class="flexItem-8" title="100 - (550*200/2770) ≈ 60.288">8</div>
<div class="flexItem-9" title="100 - (550*80/2770) ≈ 84.115 - 30*2 = 24.115">9</div>
</div>
<!--
flex_container_available_length = 1500
flex_items_length = (300+10*2) + (300+20*2+5*2) + (300) + (200+10*2) + (200+10*2+5*2) + (200+10*2) + (100+5*2) + (100+10*2+30*2) + (100+10*2)
= 2050
shrink_factor = 2770
300*2 600
300*2 600
(300-20*2-5*2)*2 500 200*1 200
200*1 200
(200-5*2)*1 190 100*2 200
100*2 200
(100-30*2)*2 80 will_allocate_length = 1500 - 2050 = -550 flex_item_width
300 - (550*600/2770) = 180.866
300 - (550*600/2770) = 180.866
300 - (550*500/2770) = 200.722 - 20*2 - 10*2 = 150.722 200 - (550*200/2770) = 160.288
200 - (550*200/2770) = 160.288
200 - (550*190/2770) = 162.274 - 5*2 = 152.274 100 - (550*200/2770) = 60.288
100 - (550*200/2770) = 60.288
100 - (550*80/2770) = 84.115 - 30*2 = 24.115
-->
</body> </html>

上面的代码包括文章中所有的 demo 的代码,和第二个问题里说的需求的解决方法(其实巨简单)

文章里留下的另一个坑,min-width 会对计算有什么影响呢?这个问题留给你自己尝试思考吧。如果想不通,也欢迎留言讨论。

最后,附上资料, 同时感谢stackoverflow的帮助。
 

你不知道的 flex-shrink 计算规则的更多相关文章

  1. F2工作流引擎参与者类型成员的交、并、互拆计算规则

          计算描述:计算规则指的是和其它“参与者类型成员”的之间的计算,必须求解处理人不为空的情况下才进行规则计算,各个“参与者类型成员”按序号顺序执行. 计算算法:并集(权重最低),交集(权重中) ...

  2. css优先级计算规则

    原文:css优先级计算规则 最近面试了一些求职者,我问css优先级计算规则是怎样的?答曰ID优先级>class>元素选择器,外联样式优先级低于内联样式,内联样式优先级低于行间样式,然后就没 ...

  3. 关于html表格单元格宽度的计算规则

    * { margin: 0; padding: 0 } body { background: #fafafa } ul,li { list-style: none } h1 { margin: 20p ...

  4. Cocos2D中节点Z序的计算规则

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交 ...

  5. CSS 选择器权重计算规则

    其实,CSS有自己的优先级计算公式,而不仅仅是行间>内部>外部样式:ID>class>元素. 一.样式类型 1.行间 <h1 style="font-size: ...

  6. MySQL Transaction--TPS计算规则

    TPS计算规则 在MYSQL 中,TPS(Transaction Per Second)的计算方法为 (com_commit+com_rollback)/time,但com_commit和com_ro ...

  7. while循环计算规则:内循环—外循环!

    num= 1 #值 =1while num <= 10 : # num(1)小于10 print(num) # 应该打印 这个1的值 num +=1 # num+=1等价于 num再加1 所以这 ...

  8. QuantLib 金融计算——基本组件之天数计算规则详解

    目录 天数计算规则详解 定义 30 / 360 法 30/360 US 30/360 Bond Basis 30E/360 30E/360 ISDA Actual 法 Actual/Actual IC ...

  9. HTML+CSS基础 权重的计算 权重计算规则

    权重的计算 将选择器上面的选择器进行叠加,叠加后的总和就是该选择器的权重. 权重计算规则

随机推荐

  1. sklearn集成支持向量机svm.SVC参数说明

    经常用到sklearn中的SVC函数,这里把文档中的参数翻译了一些,以备不时之需. 本身这个函数也是基于libsvm实现的,所以在参数设置上有很多相似的地方.(PS: libsvm中的二次规划问题的解 ...

  2. MySQL 是如何处理死锁的

    MySQL(InnoDB)是如何处理死锁的 一.什么是死锁 官方定义如下:两个事务都持有对方需要的锁,并且在等待对方释放,并且双方都不会释放自己的锁. 这个就好比你有一个人质,对方有一个人质,你们俩去 ...

  3. ES 6新语法

    一.块级作用域绑定 回顾:使用var关键字定义变量 定义 = 声明 + 赋值:   1. 可以一次定义多个变量 2. 定义时可以只声明不赋值 3. 定义之后可以随时修改变量的值 4. 变量声明会被提升 ...

  4. ElementUi中el-table分页效果

    现实的场景中很经常遇到表格el-table数据过多,为了更好的用户体验,所以我们需要用到分页,一般分页可以视数据量的大小可分为前端控制和后端控制. 先看下效果(已做脱敏处理) 图1 前端el-tabl ...

  5. String判断为空的方式

    今天遇到的笔试题: //这样的判空方式是否正确 if(!str.equals("")&&str!=null) 之前我一直都是这么写的,哪知道有什么问题呀,然后面试官 ...

  6. Appium(一):java环境、AndroidSDK环境

    1. java环境 java的下载和安装可以看我以前写的Java基础:<java下载和安装>. 2. AndroidSDK环境 2.1 AndroidSDK下载 我们进入:https:// ...

  7. qq cookie

    qq cookie from selenium import webdriver from selenium.webdriver import ActionChains import time, re ...

  8. 浅谈C++ STL string容器

    浅谈C++ STL string容器 本篇随笔简单讲解一下\(C++STL\)中\(string\)容器的使用方法及技巧. string容器的概念 其实\(string\)并不是\(STL\)的一种容 ...

  9. 解决 vscode 中 nuget 插件无法获取包版本的问题

    解决 vscode 中 nuget 插件无法获取包版本的问题 1.问题描述 大概在今年的7月份左右,我忽然发现 NuGet Package Manager 拓展没法正常使用了,只能查询到包: 选完包之 ...

  10. 手把手教你搭建织女星开发板RISC-V开发环境

    前言 Windows环境下搭建基于Eclipse + RISC-V gcc编译器的RISC-V开发环境,配合openocd调试软件,可以实现RISC-V内核程序的编译.下载和调试. 准备工作 工欲善其 ...