你不知道的 flex-shrink

对于 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的帮助。
 

原文地址:https://www.cnblogs.com/liyan-web/p/11217330.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


一:display:flex布局display:flex是一种布局方式。它即可以应用于容器中,也可以应用于行内元素。是W3C提出的一种新的方案,可以简便、完整、响应式地实现各种页面布局。目前,它已经得到了所有浏览器的支持。Flex是FlexibleBox的缩写,意为"弹性布局",用来为盒状模型提供最大的灵
1. flex设置元素垂直居中对齐在之前的一篇文章中记载过如何垂直居中对齐,方法有很多,但是在学习了flex布局之后,垂直居中更加容易实现HTML代码:1<divclass="demo">2<divclass="inner">3<p>这是一个测试这是一个测试这是一个测试这是一个测试这是一个测试</p>4</div
移动端开发知识点pc端软件和移动端apppc端软件是什么,有哪些应用。常见的例子,比如360杀毒,photoShop,VisualStudioCode等等移动端app是什么,有哪些应用。常见的例子,比如手机微信,手机qq,手机浏览器,美颜相机等等PC端与移动端的区别第一:PC考虑的是浏览器的兼容性,移动端考
最近挺忙的,准备考试,还有其他的事,没时间研究东西,快周末了,难得学点东西,grid是之前看到的,很好奇,讲的二维的布局,看起来很方便,应该很适合移动端布局,所以今天抽时间学一学,这个当是笔记了。参考的是阮老师的博客。阮一峰:CSSGrid网格布局教程http://www.ruanyifeng.com/blog/2019/03/g
display:flex;把容器设置为弹性盒模型(设置为弹性盒模型之后,浮动,定位将不会有效果)给父元素设置的属性:(1)display:flex---把容器设置为弹性盒模型。(2)flex-direction---设置弹性盒模型主轴方向默认情况下主
我在网页上运行了一个Flex应用程序,我想使用Command←组合键在应用程序中触发某些操作.这在大多数浏览器上都很好,但在Safari上,浏览器拦截此键盘事件并导致浏览器“返回”事件.有没有办法,通过Flex或通过页面上的其他地方的JavaScript,我可以告诉Safari不要这样做?解决方法:简短的
flex布局,flex-item1<template>2<viewclass="container">3<viewclass="greentxt">4A5</view>6<viewclass="redtxt">7B8<
我应该设计一个大型多点触控屏幕的应用程序.从大到大,我的意思是新闻广播员(大约55英寸及以上).该应用程序是一个交互式地图.我的问题是:开发应用程序的技术.我的第一个想法是在AdobeFlex中制作,但是HTML5也是如此……必须有一些非常棒的Java库用于触摸交互,但是在Windows平台上
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><metahttp-equiv="X-UA-Compatible&quo
【1】需求:  【2】解决方案:最近遇到布局上要求item两端对齐,且最后一行在列不满的情况下要求左对齐,使用flex的justify-content:space-between;实现时发现最后一行不能左对齐,而是两端对齐方式。 不是项目上想要的效果#网上查了一些资料,有两种方法可以实现效果:**1.
我有一个java套接字服务器,它在连接时将Animal对象发送到Flash客户端.对象发送方式如下:Amf3Outputamf3Output=newAmf3Output(SerializationContext.getSerializationContext());amf3Output.setOutputStream(userSocket.getOutputStream());amf3Output.writeObject(animal)
我正在开发一个Flex3.4应用程序,它通过最新版本的BlazeDS与JBoss-4.2.2服务器上运行的JavaEE后端进行交互.当我在Tomcat上从FlashBuilder4beta2运行Flex应用程序时,一切都很好,Flex应用程序能够进行所需的远程调用.但我的生产环境是在JBoss上,当我将应用程序移动到JBoss时(更
我有一个非常大的问题.我使用Flex3/Tomcat/BlazeDS/Spring编写了一个大型应用程序,在本地开发时运行良好,当我部署到公共开发环境时很好,但是当部署到我们的测试环境时经常失败.当远程处理请求花费大量时间(超过20秒)时,故障似乎最常发生.在我的开发服务器上,错误发生,但仅
弹性和布局display:flex在ie6,ie7不兼容状态,一般在pc用的比较少,在手机端所有的浏览器都是支持的控制子元素在父元素里面的位置关系display:flex是给父元素加的文档流是按照主轴排列,只要父元素加了flex,那么里面的子元素全部可以直接添加宽高主轴的方向
FLEX2.0源码分析(一)https://www.jianshu.com/p/8bc4c5f4b19fFLEX源码分析二(网络监测swizzle)https://www.jianshu.com/p/ffb95f2cbda6FLEX源码分析三(网络监测记录FLEXNetworkRecorder)https://www.jianshu.com/p/66267dc922c5FLEX源码分析四(Systemlog)https://www.jianshu.
1<!DOCTYPEhtml>2<htmllang="en">3<head>4<metacharset="UTF-8">5<title><itle>6<style>7*{8margin:0;9padding:0;10
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><metahttp-equiv="X-UA-Compatible&qu
flex:将对象作为弹性伸缩盒显示inline-flex:将对象作为内联块级弹性伸缩盒显示两者都是使子元素们弹性布局,但是如果是flex,父元素的尺寸不由子元素尺寸动态调整,不设置时默认是100%,而inline-flex则会使父元素尺寸跟随子元素们的尺寸动态调整。
<html><head><metacharset="utf-8"><metaname="viewport"content="width=device-width"><title>test<itle><stylemedia="screen">.tab-head{list-style-type:no
有没有办法使用邮政编码找到径向距离?我的任务是搜索居住在指定距离内的所有用户.我知道用户的zipcodes.例如,距离当前位置25英里的用户.我有其他搜索类别,我正在使用mysql查询.我无法解决距离问题.我的后端是在PHP中Flex的前端和前端.对我来说最好的选择就是www.zip-codes.com