最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 深入css的flex排版原理

    正文概述 掘金(army8735)   2021-04-14   409

    前言

    在经历过table布局、div布局之后,flex逐渐完善且成为标准。这时候前端排版迎来了一次解放,很多难以书写的布局写法被大大简化了。时至今日,flex早已成为一个基础,无论是功能还是技能方面。它涵盖了日常开发中绝大部分场景。

    本文不再讨论flex语法、功能和缩写,直接讲解flex如何实现即算法原理部分。对于应用方面还不了解的请先自行查阅。

    准备

    和其它普通文档流(如block)不同的是,flex的子节点的顺序比较特殊,其按照css声明的order顺序排列,如果没有或相等则降级为节点序。因此第一步是把容器的文档流子节点先排序,结果记为orderChildren

    我们叫flex为弹性布局的根本原因是每个子节点都是弹性的,它可以伸缩(grow/shrink),这样势必有一个基准值(basis)作为参考,如此才能在一个基础上决定伸缩情况。那么这个基准值怎么决定呢? www.w3.org/TR/css-flex…

    根据规范,我们可得知(进行了一定程度简化,如不考虑min/max限制、竖排版文字、英文单词排版等):

    1. 如果节点定义了flex-basis,则使用它;
    2. 如果节点是具有固定宽高比的特殊节点(如img),且flex-basis最终是content,且交叉轴已知,那么根据宽高比计算后的主轴尺寸就是;
    3. auto如果定义了主轴尺寸则使用它,如果没有则降级为content
    4. content为内容自适应后的尺寸。

    上面自适应是最难的,因为内容是不固定的,且内容本身的布局方式也是变化的。因此自适应需要有个计算内容尺寸有多大的过程(其实即便不是自适应也需要计算下面说的max/min)。

    在了解这个过程之前,需要先知道2个基本概念:

    1. 最大尺寸(记max):指节点在当前环境下理想状态的尺寸;
    2. 最小尺寸(记min):指节点在当前环境下被压缩到最小状态的尺寸;

    文字描述有点抽象,看图示例:

    深入css的flex排版原理

    左边即第1点max,它假设可用空间是无限的,因此文本(或inline节点等)可以在一行内全部排下,即便超过flex父节点(黑边)也无所谓;

    右边即第2点min,它假设空间完全不够用,但为了保证内容渲染至少要有单个字符(或英文单词等)的宽度,也不考虑是否超过flex父节点。

    回到计算自适应的过程,我将它分为2个大部分:

    1. flex直接子节点的尺寸计算;
    2. 其后续递归子节点的尺寸计算。

    先看第1大部分。flex的子节点是个匿名块容器,可认为强制是块级(block/flex,忽略inline-flex)。

    当子节点是block时,我们需要结合flex-direction(row/column)和递归子节点的类型(dom/text)来计算。它比较简单,在row情况下,先对block的子节点(此时关系层级为flex的孙子节点,下面都将简称孙子节点)进行上述第2大部分的计算,获取到max2/min2,然后看孙子节点的display,block则独占一行,inline和文字则尝试理想状态下同行,如此便能获取到max/min的最大值(所有max相比最大,所有min相比最大)。basis最终就是max的值。

    在column的情况下差不多,所有block类型的孙子节点独占一行从上到下排列时max/min是累加的,而inline和文字则是取同行里高度最大的那个。

    当子节点是flex时,情况和上面block也类似,不过更加简化,因为flex子节点强制块级,因此孙子节点中没有inline更加容易计算。

    再看第2大部分。它和第1大部分的区别在于节点可以有inline类型,其余部分一样,整体计算方式类雷同。

    至此,我们获取了每个flex子节点的max/min值。

    分行

    接下来根据每个子节点的情况求假设主尺寸,这步比较简单,根据basis/max/min这3者之间的关系即可,用公式表达为:

    hypothetical = clamp(min_main_size, flex_base_size, max_main_size)
    

    伪代码形式为:

    hypothetical_main_size:
    if flex_base_size > max_main_size:
        return max_main_size
    if flex_base_size < min_main_size:
        return min_main_size
    return flex_base_size
    

    根据flex-wrap的声明,我们要判断容器内布局是单行(nowrap)还是多行(wrap),反向多行(wrap-reverse)和多行相同,只是顺序颠倒。那么怎么判断呢?用上面求得的假设主尺寸将orderChildren依次排布,如果一行放不下则另起一行。当然如果声明是nowrap不需要换行(即只有一行),但无论哪种都需要统计每行的假设主尺寸之和。

    布局

    现在得到每一行的信息了,利用官方文档给定的如下算法:

    1. 确定使用的弹性因子。对所有项的外部假设主尺寸求和,如果和小于flex容器的主尺寸,则算法使用增长因子,否则使用收缩因子。即看用flex-grow还是flex-shrink。
    2. 非弹性尺寸项。冻结,设置它的目标主尺寸为其假设主尺寸。满足任意下列条件即为非弹性尺寸项:
    • a. 弹性因子为0
    • b. 如果使用增长因子,flex-basis计算值大于其假设主尺寸
    • c. 如果使用收缩因子,flex-basis计算值小于其假设主尺寸
    1. 计算初始可用空间。对行上所有项的外部尺寸求和,再被减去flex容器的主尺寸。对于冻结项目来说,外部尺寸是指目标主尺寸;其它非冻结的则为其外部flex-basis计算值。
    2. 循环:
    • a.检查每一项。如果所有的弹性项都被冻结了,则可用空间视为分配完毕,跳出循环。
    • b.计算剩余可用空间并将它作为上条中提到的初始可用空间。如果未冻结的项的弹性因子之和小于1,则将初始可用空间乘以此和。如果这个值小于剩余可用空间,则设置它为新的剩余可用空间。
    • c.根据弹性因子分配可用空间。

    如果剩余可用空间是0 跳过。 如果使用增长因子 计算该项的增长因子占所有未冻结项的增长因子之和的比例。设置该项的目标主尺寸为flex-basis计算值加上比例乘以剩余可用空间。 如果使用收缩因子 对未冻结的每项,将其收缩因子和flex-basis计算值相乘,记为缩放收缩因子。求得所有缩放收缩因子的和,然后得出每项缩放收缩因子占和的比例。将项的目标主尺寸设置为flex-basis计算值减去比例乘以剩余可用空间的积。注意,这可能会导致主尺寸为负值,下一步将修正。 否则 跳过。

    • d.修正最小/最大值违规问题。将每个非冻结项的目标主尺寸按其使用的最小/最大值固定(clamp函数的意思),限制其content-box为0。如果目标主尺寸小于最小值,则为最大违规。反之大于最大值,则为最小违规。
    • e.冻结过渡弹性伸缩的项。总的违规数值为上一步中每项调整的综合 ∑(clamped size - unclamped size),即违规差值。如果这个数值是:

    0 冻结所有项。 正数 冻结所有最小违规项。 负数 冻结所有最大违规项。 5. 设置每项的主尺寸为其目标主尺寸。

    另外值得注意的是,规范中没有详细提及orderChildren的mpb(margin/padding/border),以及递归孙子节点的mpb。直接item的无论单位如何都是考虑在内的;而孙子节点只考虑进min/max尺寸,且必须是固定值,其它如百分比则忽略。

    反向

    当反向多行时,需要多处理一步,将当前正向多行的内容进行倒序排列,注意单位是行,下面将以row举例。 记正向每行高度列表是maxCrossList,行首为相对起点即y=0。统计出递增的高度列表crossSumList,即前面所有行高度之和。 然后从末尾开始循环,设一个count变量,每次循环结束增加maxCrossList对应索引的值。记sourcecrossSumList对应索引的值,记diffcount减去source的值,如果不为0,则进行偏移。

    示例

    <div style={{display:'flex',width:100}}>
      <span style={{flex:'1 1 50',background:'#F00',padding:'0 5'}}>2</span>
      <span style={{flex:'1 1 40',background:'#00F'}}>3</span>
    </div>
    <div style={{display:'flex',width:100}}>
      <span style={{flex:'1 1 auto',background:'#F00'}}><strong style={{display:'block',padding:'0 5'}}>2</strong></span>
      <span style={{flex:'1 1 auto',background:'#00F'}}><strong style={{display:'block'}}>3</strong></span>
    </div>
    <div style={{display:'flex',width:100}}>
      <span style={{flex:'1 1 auto',background:'#F00'}}><strong style={{display:'block',padding:'0 5%'}}>2</strong></span>
      <span style={{flex:'1 1 auto',background:'#00F'}}><strong style={{display:'block'}}>3</strong></span>
    </div>
    

    我们用这3个简化的例子来看过程,最终效果如下:

    深入css的flex排版原理

    3个flex节点按顺序称之为A、B、C,它们很相似,区别在于mpb以及子节点。 A节点的孩子们basis为50和40,但由于首个声明了padding,所以最终是60和40,max同,min是19和9左右(字符2和字符3的尺寸,首个要算padding)。假设主尺寸和basis保持一致。因为60+40正好=100,所以最终他们就是60和40的宽度。

    B节点的孩子们basis为auto,且没有width声明,所以降级为content自适应。首个节点的递归子节点多了padding,所以最终max、min和basis是19和9左右。假设主尺寸和basis保持一致,空余100-19-9=72,均分给2个节点每个36,最终他们是55和45的宽度。

    C节点的孩子们basis为auto,且没有width声明,所以降级为content自适应。首个节点的递归子节点多了padding但百分比无效,所以最终max、min和basis都是9左右。假设主尺寸和basis保持一致,空余100-9-9=82,均分给2个节点每个41,最终他们都是50的宽度。

    最后附上源码:github.com/karasjs/kar…


    起源地下载网 » 深入css的flex排版原理

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元