对于 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. 什么是 AQS?简单说一下 ReentrantLock 的原理?

    AQS 简介 java的内置锁一直都是备受争议的,在JDK 1.6之前,synchronized这个重量级锁其性能一直都是较为低下,虽然在1.6后,进行大量的锁优化策略,但是与Lock相比synchr ...

  2. 5.Ansible Jinja2 模板

    1.jinja2渲染NginxProxy配置文件 jinja2 房屋建筑设计固定的 jinja2模板与Ansible关系 Ansible如何使用jinja2模板 template模块 拷贝文件? te ...

  3. JS---DOM---总结获取元素的方式

    总结获取元素的方式 1. 根据id属性的值获取元素,返回来的是一个元素对象 document.getElementById("id属性的值"); document.getEleme ...

  4. SQL Server启动/关闭xp_cmdshell

    ==>启用xp_cmdshell USE master RECONFIGURE WITH OVERRIDE RECONFIGURE WITH OVERRIDE RECONFIGURE WITH ...

  5. 微信小程序连接低功率蓝牙控制单片机上硬件设备

    1.软件部分介绍 微信小程序是一种新的应用,用户不需要下载应用只用通过扫二维码或者打开链接就能使用,使用完后不需要卸载,直接关闭就行了.微信在2017年初推出微信小程序开发环境.任何企业,媒体,个人都 ...

  6. Goland快捷键(Macbook)

    Goland快捷键(Macbook) 基础编辑快键键 向上或向下移动当前行 ⇧⌘↑ ⇧⌘↓ 复制并粘贴当前选中的语句 ⌘D 删除当前行 ⌘⌫ 行注释 ⌘/ 块注释 ⌥⌘/ 在当前打开的文件中寻找 ⌘F ...

  7. centos7在线yum安装mysql时官方镜像下载过慢的解决方案

    帮客户调试数据库,搭建一测试环境,centos7最小化安装后,在线安装mysql. 步骤: 1. wget -i http://dev.mysql.com/get/mysql57-community- ...

  8. mybatis+sqlServer 实现insertOrUpdate

    这两天遇到一个头疼的问题,我们系统需要请求第三方数据,第三方收到请求后会生成相应的数据并入库,我们通过定时任务将第三方数据同步到我们数据库.当我们发送请求后第三方会立即返回一个值,我们会根据返回值去数 ...

  9. sqlplus登录时密码有特殊符号解决方法

    偶然百度得到解决方法,在查看了公司有的脚本使用了这种解决方式,特记录下来,有需要的可以点击文末的链接查看原文. 本文转载https://www.cnblogs.com/lhrbest/p/656090 ...

  10. 检测值是否存在(??)(Freemarker的null值处理)

    使用形式: unsafe_expr?? 或 (unsafe_expr)?? 这个操作符告诉我们一个值是否存在.基于这种情况, 结果是 true 或 false. 访问非顶层变量的使用规则和默认值操作符 ...