控制Flex子元素在主轴上的比例的方法

所属分类: 网页制作 / CSS 阅读数: 1525
收藏 0 赞 0 分享

背景

flex布局更有效的实现对齐,空间分配。最近又学习下flex子元素的尺寸计算规则,主要是flex-grow, flex-shrink的计算规则的学习。

一、基本概念

1.1 主轴(Main axis)

定义了flex元素布局起始点和方向,flex子元素在主轴上依次放置。

主轴有4个方向,通过flex-direction指定:

  • row

水平方向,从左到右,默认的

  • row-reverse

水平方向,从右到左

  • column

垂直方向,从上到下

  • column-reverse

垂直方向,从下到上

1.2 主轴的尺寸(Main axis size)

就是flex容器content矩形(不包含padding, border, margin区域)在主轴方向的尺寸。

1.3 交叉轴(Cross axis)

交叉轴就是跟主轴锤子的方向,主要用于flex元素的对齐。

1.4 交叉轴的尺寸(Cross axis size)

就是flex容器content矩形(不包含padding, border, margin区域)在Cross轴方向的尺寸。

1.5 flex盒模型(flex box)

display为flexinline-flex,的元素,也叫flex容器。

1. flex容器包含的不仅是flex元素,也包含空白空间。

2. 涉及的CSSflex-directionflex-wrapflex-flow

  • flex-direction
  • flex-wrap
  • flex-flow

flex-direction和 flex-wrap的简写。

  • justify-content

控制flex容器内容(flex元素和空白空间)在主轴方向对齐。注意区分align-items。

  • align-content

控制多行flex容器个行的对齐方式。

  • align-items

控制flex容器内容(flex元素和空白空间)在交叉轴方向对齐。

Tip:

1、这些CSS属性都是有相关性的:

首页先指定flex容器的主轴方向(flex-direction), 如果flex子元素超过在主轴 尺寸,那就涉及是否换行(flex-wrap)。如果没有超过主轴尺寸,那就涉及行内对齐(justify-content), 如果存在多行每个行直接也要对齐(align-content)。

2、可能比较容易混淆 justify-content,align-content,align-items。

记住content是指flex元素和空白空间,items指的是flex元素。这样就容易就是这三个属性的用处了。

1.6 flex元素(flex items)

1. 语法

flex box的子元素,不包含流外子元素( absolute, fix元素),但是包含float元素。

flex子元素相邻的margin不会发生合并。

float元素作为flex子元素时,float属性无效(因为要参与flex布局,不是流式布局)。

2. 涉及CSS属性

  • flex-basis
     

指定flex元素在主轴方向上占用flex容器的尺寸,默认为auto,即以flex元素的尺寸作为其占用的尺寸(主轴是row取值flex元素的宽度,主轴是column取值flex元素的高度),根据该属性计算是否有空余空间。

注意:flex元素占用的尺寸跟flex-basis有关,跟元素的宽高没直接关系。

  • flex-grow
     

指定各个flex元素弹性增长因数,即占用正空白空间(positive free space)的比例份额数量。0值(默认值)表示不占用空白空间

  • flex-shrink
     

指定各个flex元素弹性收缩因数,即分配负空白空间(negative free space)的比例份额数量。但是元素不能无限收缩为0,也不能小于元素的最小尺寸(如min-width或者min-height)。

  • flex

flex-grow flex-shrink flex-basis简写

  • align-self

调整自己在交叉轴的对齐方式,只有在不撑满交叉轴时,这个属性才有效。

  • order

指定顺序

二、计算自由空间和flex-basis

flex子元素在主轴上的比例依赖这三个CSS属性:

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

其中:
flex-basis + flex-grow组合控制着伸
flex-basis + flex-shrink组合控制着缩
所以要确定flex子元素在主轴上的比例,首先要确定使用哪种组合。

2.1 规则

flex-basis 属性指定在任何空间分配发生之前初始化flex子元素的尺寸,更确切的说flex-basis 属性指的flex子元素盒模型(box-sizing)的尺寸,所以跟flex子元素width(height)取值逻辑类似,如果box-sizing=content,则flex-basis也不包含padding和border区域。

2.2 剩余自由空间计算

自由空间计算
flex容器在主轴方向的content矩形的尺寸

期望自用空间
在计算flex容器的自由空间前要先统计flex子元素占用的尺寸,注意这里指的是flex子元素的margin区域的尺寸,并且相邻的flex子元素margin是不会发生合并的。

剩余自由空间计算 = 自由空间计算 - 期望自用空间

正自由空间
正值的剩余自由空间,此时采用flex-basis + flex-grow组合。

负自由空间
负正值的剩余自由空间,此时采用flex-basis + flex-shrink组合。

三、深入了解flex-grow

3.1 规则

如果存在正自由空间(positive free space),则采用flex-basis + flex-grow组合计算flex子元素在主轴上的比例。把正自由空间比作蛋糕的话,flex-grow表示希望分得蛋糕的量:

  • flex-grow: 0.2 表示希望获得20%的蛋糕;
  • flex-grow: 1 表示希望获得100%整个蛋糕(有点过分啊,不考虑其他兄弟);
  • flex-grow: 2 表示希望获得200%的蛋糕(这是明抢啊,态度很明确)。

但毕竟蛋糕就一个,flex容器尽量满足felx子元素的要求,采用一种简单的按照比例分蛋糕方式:

  • 累加flex子元素的flex-grow得出总和,简称SUM_flex_grow;
  • 如果SUM_flex_grow=0,则不发生弹性增长,结束;
  • flex子元素增长的尺寸 = 正自由空间尺寸 * flex_grow / Max(SUM_flex_grow, 1)

3.2 Demo1:按照比例分蛋糕

function demo1() {
    return (
        <>
            <div className="flex">
                <div className="item" style={{flexBasis: 100, flexGrow: 1, marginRight: 10}}>One</div>
                <div className="item" style={{flexBasis: 150, flexGrow: 2, }}>Two</div>
            </div>
            <style jsx>{`
                .flex {
                    display: flex;
                    width: 600px;
                    outline: 1px dashed red;
                }
                .item {
                    padding: 10px;
                    border: 10px solid #666666;
                }
            `}</style>
        </>
    )
}

解析:

计算剩余自由空间

  • flex容器主轴尺寸 = 600px
  • 元素one的希望尺寸 = 100px(flex-basis) + 20px(padding-left/right) + 20px(border-left/right) + 10px(margin-right) = 150px
  • 元素two的希望尺寸 = 150px(flex-basis) + 20px(padding-left/right) + 20px(border-left/right) = 190px
  • 剩余自由空间 = 600px - 150px - 190px = 260px,即存在正剩余空间。

计算各个flex子元素增长尺寸

  • SUM_flex_grow = 1 + 2 = 3,即大于1 ,一个蛋糕不够分,只能按照比例分了。
  • 元素one的实际增长尺寸 = 260px * 1 / Max(1, 1 + 2) = 260px * 1 / (1 + 2) = 86.67px
  • 元素two的实际增长尺寸 = 260px * 2 / Max(1, 1 + 2) = 260px * 2 / (1 + 2) = 173.33px

3.3 Demo2:SUM(flex-grow) < 1

function demo3() {
    return (
        <>
            <div className="flex">
                <div className="item" style={{flexBasis: 100, flexGrow: 0.2, marginRight: 10}}>One</div>
                <div className="item" style={{flexBasis: 150, flexGrow: 0.3, }}>Two</div>
            </div>
            <style jsx>{`
                .flex {
                    display: flex;
                    width: 600px;
                    outline: 1px dashed red;
                }
                .item {
                    padding: 10px;
                    border: 10px solid #666666;
                }
            `}</style>
        </>
    )
}

解析:

计算剩余自由空间

  • flex容器主轴尺寸 = 600px
  • 元素one的希望尺寸 = 100px(flex-basis) + 20px(padding-left/right) + 20px(border-left/right) + 10px(margin-right) = 150px
  • 元素two的希望尺寸 = 150px(flex-basis) + 20px(padding-left/right) + 20px(border-left/right) = 190px
  • 剩余自由空间 = 600px - 150px - 190px = 260px,即存在正剩余空间。

计算各个flex子元素增长尺寸

  • SUM_flex_grow = 0.2 + 0.3 = 0.5,即小于1 ,一个蛋糕能满足大家需求,直接分给各个flex子元素。
  • 元素one的实际增长尺寸 = 260px * 0.2 / Max(1, 0.5) = 260px * 0.2 = 52px
  • 元素two的实际增长尺寸 = 260px * 0.3 / Max(1, 0.5) = 260px * 0.3 = 78px

注意:

如果SUM(flex-grow)小于1,此时剩余空间没有全部分配给各个flex子元素。

3.3 Demo3 跟max-width冲突

留意该栗子中:

  • 元素one, two, threebox-sizing=border-box
  • 元素one的max-width=150px
function demo4() {
    return (
        <>
            <div className="flex">
                <div className="item" style={{flexBasis: 100, flexGrow: 1, marginRight: 10, maxWidth: 150}}>One</div>
                <div className="item" style={{flexBasis: 150, flexGrow: 2 }}>Two</div>
                <div className="item" style={{flexBasis: 100, flexGrow: 3 }}>Three</div>
            </div>
            <style jsx>{`
                .flex {
                    display: flex;
                    width: 800px;
                    outline: 1px dashed red;
                }
                .item {
                    padding: 10px;
                    border: 10px solid #666666;
                    box-sizing: border-box;
                }
            `}</style>
        </>
    )
}

解析:

计算剩余自由空间

flex容器主轴尺寸 = 800px

元素one的希望尺寸 = 100px(flex-basis) + 10px(margin-right) = 110px
box-sizing=border-box

元素two的希望尺寸 = 150px(flex-basis) = 150px
box-sizing=border-box

元素three的希望尺寸 = 150px(flex-basis) = 150px
box-sizing=border-box

剩余自由空间 = 800px - 110px - 150px - 150px = 390px,即存在正剩余空间。

计算各个flex子元素增长尺寸

  • SUM_flex_grow = 1 + 2 + 3 = 6,即大于1 ,一个蛋糕不够分,只能按照比例分了。。
  • 元素one的增长尺寸 = 390px * 1 / Max(1, 6) = 390px * 1/6 =65px
     

这样元素one的尺寸就是100px + 65px = 165px,大于其max-width=150px指定的最大值,所以最终元素one的尺寸是150px。即元素one吃不完分配的蛋糕,把吃不完的蛋糕还回去了,让其他兄弟多分些(先抛个问题:这些吃不完的蛋糕如何分配呢?)。

元素two和元素three重新分配剩下是自由剩余空间,即回到步骤1重新计算。

  • flex容器主轴尺寸 =800px - 元素one占领的尺寸(150px - 10px) = 640px
  • 剩余空间 = 640px - 150px - 150px = 340px
  • SUM_flex_grow = 2 + 3 = 5
  • 元素two的增长尺寸 = 340px * 2 / Max(1, 5) = 340px * 2 / 5 = 136px
  • 元素three的增长尺寸 = 340px * 3 / Max(1, 5) = 340px * 3 / 5 = 204px

3.4 小结:

  • 计算剩余自由空间永远是第一步;
  • 增长是个绝对值,即flex子元素会增加个绝对值(这是跟flex-shrink不同的地方);
  • 当遇到max-属性冲突时,即元素one吃不完的蛋糕会放入总蛋糕中,由后面的flex子元素重新分配。

四、深入了解flex-shrink

4.1 规则

如果存在负自由空间(negative free space),则采用flex-basis + flex-shrink组合计算Flex子元素在主轴上的比例。flex-shrink取值表达了个flex子元素贡献的愿望:

  • flex-shrink: 0.2 表示希望分摊负自由空间的20%;
  • flex-shrink: 1 表示希望分摊100%负自由空间(够兄弟,其他兄弟不用分摊);
  • flex-shrink: 2 表示希望分摊200%负自由空间(分摊的态度很明确)。

flex容器都感动哭了,但为了照顾各个flex子元素的感受,采用了一个“更合理”的分摊规则:

  • 计算flex子元素的content矩形(内容矩形)在主轴尺寸 和 flex-shrink乘积值,记作A;
  • 累加步骤1的乘积值,记作SUM_A;
  • 被分摊的负自由空间 valid_negative_free_space = negative_free_space * Min(1, SUM(flex-shrink))
  • 每个flex子元素的收缩值 = valid_negative_free_space * A / SUM_A

计算的规则比上面的要复杂一些,不是简单的切分negative-free-space。收缩量不仅依赖flex-shrink,还依赖flex-basis。这样做只是为了“更合理”,即相同的flex-shrink情况下,flex-basis越小的flex元素收缩的越慢(跟纳税一样,收入越高交的越多)。

注意: 如果flex-shrink总和小于1,则表示部分负自由空间被分摊了(即有些尺寸没有被收缩)。

4.2 Demo1:“减少贫富差距”

function demo5() {
    return (
        <>
            <div className="flex">
                <div className="item" style={{flexBasis: 100, flexGrow: 1, marginRight: 10}}>One</div>
                <div className="item" style={{flexBasis: 150, flexGrow: 2, flexShrink: 2 }}>Two</div>
            </div>
            <style jsx>{`
                .flex {
                    display: flex;
                    width: 300px;
                    outline: 1px dashed red;
                }
                .item {
                    padding: 10px;
                    border: 10px solid #666666;
                }
            `}</style>
        </>
    )
}

解析(过长跟flex-grow过程类似):

计算剩余自由空间

  • flex容器主轴尺寸 = 300px
  • 元素one的希望尺寸 = 100px(flex-basis) + 20px(padding-left/right) + 20px(border-left/right) + 10px(margin-right) = 150px
  • 元素two的希望尺寸 = 150px(flex-basis) + 20px(padding-left/right) + 20px(border-left/right) = 190px
  • 剩余自由空间 = 300px - 150px - 190px = -40px,即存在负剩余空间。
  • 被分摊的负剩余空间 = -40px * Min(1, 1 + 2) = -40px

计算各个flex子元素收缩尺寸

  • SUM_A = 100px * 1 + 150px * 2 = 400px
  • 元素one的实际收缩尺寸 = 40px * 100px * 1 / 400px= 10px,即最终宽度 = 100px - 10px = 90px
  • 元素two的实际收缩尺寸 = 40px * 150px * 2 / 400px = 30px,即最终宽度 = 150px - 30px = 120px

4.2 Demo: SUM(flex-shrink) < 1

function demo8() {
    return (
        <>
            <div className="flex">
                <div className="item" style={{flexBasis: 100, flexShrink: 0.2, marginRight: 10}}>One</div>
                <div className="item" style={{flexBasis: 150, flexShrink: 0.3 }}>Two</div>
            </div>
            <style jsx>{`
                .flex {
                    display: flex;
                    width: 300px;
                    outline: 1px dashed red;
                }
                .item {
                    padding: 10px;
                    border: 10px solid #666666;
                }
            `}</style>
        </>
    )
}

flex子元素超出了flex容器。

解析:

计算剩余自由空间

  • flex容器主轴尺寸 = 300px
  • 元素one的希望尺寸 = 100px(flex-basis) + 20px(padding-left/right) + 20px(border-left/right) + 10px(margin-right) = 150px
  • 元素two的希望尺寸 = 150px(flex-basis) + 20px(padding-left/right) + 20px(border-left/right) = 190px
  • 剩余自由空间 = 300px - 150px - 190px = -40px,即存在负剩余空间。
  • 有效负剩余空间 = -40px * Min(1, 0.2 + 0.3) = -40px * 0.5 = -20px

计算各个flex子元素收缩尺寸

  • SUM_A = 100px * 0.2 + 150px * 0.3 = 65px
  • 元素one的实际收缩尺寸 = 20px * 100px * 0.2 / 65px= 6.15px,即最终宽度 = 100px - 6.15px = 93.85px
  • 元素two的实际收缩尺寸 = 20px * 150px * 0.3 / 65px= 13.85px,即最终宽度 = 150px - 13.85px = 136.15px

4.4 Demo3: box-sizing =border-box

留意:元素one, twobox-sizing= border-box

function demo6() {
    return (
        <>
            <div className="flex">
                <div className="item" style={{flexBasis: 100, flexGrow: 1, marginRight: 10}}>One</div>
                <div className="item" style={{flexBasis: 150, flexGrow: 2, flexShrink: 2 }}>Two</div>
            </div>
            <style jsx>{`
                .flex {
                    display: flex;
                    width: 200px;
                    outline: 1px dashed red;
                }
                .item {
                    padding: 10px;
                    border: 10px solid #666666;
                    box-sizing: border-box;
                }
            `}</style>
        </>
    )
}

解析:

计算剩余自由空间

  • flex容器主轴尺寸 = 200px
  • 元素one的希望尺寸 = 100px(flex-basis) + 10px(margin-right) = 110px
  • 元素two的希望尺寸 = 150px(flex-basis) = 150px
  • 剩余自由空间 = 200px - 110px - 150px = -60px,即存在负剩余空间。
  • 被分摊的负剩余空间 = -60px * Min(1, 1 + 2) = -60px

计算各个flex子元素收缩尺寸

  • SUM_A = 60px * 1 + 110px * 2 = 280px
  • 注意:此时不是直接用flex-basis去乘flex-shrink。本质上是使用flex子元素的content矩形宽度值去乘flex-shrink。
  • 元素one的实际收缩尺寸 = 60px * 60px * 1 / 280px = 12.86px,即最终宽度 = 60px - 12.86px = 47.14px(不包含padding,border)
  • 元素two的实际增长尺寸 = 60px * 110px * 2 / 280px = 47.14px,即最终宽度 = 110px - 47.14px = 62.86px(不包含padding,border)

4.5 Demo5 跟min-width冲突

留意该栗子中:

元素one的min-width=60px

function demo7() {
    return (
        <>
            <div className="flex">
                <div className="item" style={{flexBasis: 100, flexShrink: 2, marginRight: 10, minWidth: 60}}>One</div>
                <div className="item" style={{flexBasis: 150, flexShrink: 2 }}>Two</div>
                <div className="item" style={{flexBasis: 100, flexShrink: 1 }}>Three</div>
            </div>
            <style jsx>{`
                .flex {
                    display: flex;
                    width: 300px;
                    outline: 1px dashed red;
                }
                .item {
                    padding: 10px;
                    border: 10px solid #666666;
                }
            `}</style>
        </>
    )
}

解析:

计算剩余自由空间

  • flex容器主轴尺寸 = 300px
  • 元素one的希望尺寸 = 100px(flex-basis) + 20px(padding-left/right) + 20px(border-left/right) + 10px(margin-right) = 150px
  • 元素two的希望尺寸 = 150px(flex-basis) + 20px(padding-left/right) + 20px(border-left/right)= 190px
  • 元素three的希望尺寸 = 100px(flex-basis) + 20px(padding-left/right) + 20px(border-left/right) = 140px
  • 剩余自由空间 = 300px - 150px - 190px - 140px = -180px,即存在负剩余空间。
  • 被分摊的负剩余空间 = -180px * Min(1, 1 + 2 + 2) = -180px

计算各个flex子元素收缩尺寸

  • SUM_A = 100px * 2 + 150px * 2 + 100px * 1 = 400px
  • 元素one的实际收缩尺寸 = 180px(负剩余空间的绝对值) 100px 2 / 700px = 51.43px,
  • 这样元素one的尺寸最100px - 51.43px = 48.57px,小于其min-width=60px,即最终宽度为60px。即分配给元素one的税负需要由其他兄弟分摊了。

元素two和元素three重新分配剩下是自由剩余空间,即回到步骤1重新计算。

  • flex容器主轴尺寸 = 300px - 元素one占领的尺寸(60px + 20px + 20px + 10px) = 190px
  • 剩余空间 = 190px - 190px - 140px = -140px,即元素two,three要总缩减140px。
  • SUM_A = 150px * 2 + 100px * 1 = 400px
  • 元素two的收缩尺寸 = 140px * 150 * 2 / 400px = 105px,即最终宽度 = 150px - 105px = 45px
  • 元素three的收缩尺寸 = 140px * 100 / 400px = 35px,即最终宽度 = 100px - 35px = 65px

4.6 小结

  • 缩减的规则稍稍复杂些,这背后是有原因的,主要防止宽度小的元素缩减太快导致为负宽度。
  • flex子元素发生弹性伸缩只是content矩形,其margin,border, padding不会发生弹性伸缩的,所以他们也不参与弹性伸缩的计算公式内(如弹性收缩的公式)
  • 当遇到min-属性冲突时,即元素不能再收缩时,由后面的flex子元素重新分摊剩余空间。除了min-属性指定最小尺寸时,每个元素都存在最小尺寸的。

参考

css-tricks: A Complete Guide to Flexbox
规范
Understanding flexbox

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

更多精彩内容其他人还在看

CSS教程:CSS命名参考

在XHTML中定义ID、CLASSS都用得上,主要是方面CSS定义样式时能一眼看穿。所以,CSS命名仅作参考。 (1)页面结构类 容器: container 页头:header 内容:content/container 页面主体:main 页尾:footer 导航:na
收藏 0 赞 0 分享

CSS教程:控制网页文件大小通过精简CSS实现

尽管对于现如今的带宽来说,网页文件那仅以K来算的大小实在是微不足道,但如何将这以K来计算的网页文件精简到最小还是网页设计师们所应该考虑的问题之一。 众所周之,在不影响整个网页构架与功能的情况下,网页文件越小越好,因为更小的网页文件有利于浏览器对网页的解释
收藏 0 赞 0 分享

CSS教程:关于文字溢出问题的研究

首先引起对这个溢出问题进行研究是因为看到一个朋友的帖子,里面提到ie7下出现的文字溢出问题; 于是又重新翻了以前怿飞斑竹的帖子,他提到的是注释引起的文字溢出问题,我今天看的时候发现在ie7下并没有产生多猪的问题(以前我没有装ie7,所以没有测试),今天看到这个新
收藏 0 赞 0 分享

符合web标准的嵌入Flash的方法

  常有网友提问,如何让网页中嵌入的Flash标签也符合web标准。目前还没有一个完美的解决办法,这篇文章中,我们将Flash嵌入标签写入js文件中,通过变量传递参数的办法来回避不符合标准的标签。   请注意,这只是一个变通的方法,换汤不换药,并未能最终解决存在的
收藏 0 赞 0 分享

学习WEB标准必备的四项技能

今天在群里,熊猫君提议整理一个帖子,一方面为初学者提供一个入门指南,另一方面也象借此和已经在从事这个行业进行一点交流。下面是我从事这个行当多年的一些经验总结,希望抛砖引玉,大家不吝赐教。 1、必备工具 其实web标准并不是很复杂的技术,实现web标准的工具
收藏 0 赞 0 分享

CSS对表格单元格强制换行和不换行

CSS控制Table单元格强制换行与强制不换行   我们知道Div的换行和不换行的css写法。但对于表格单元格只知道一个属性nowrap可以使其不换行。   近日有此需要,但发现加上nowrap在某些情况下还是会换行!无奈,没有一个强制不换行的方法吗?   用C
收藏 0 赞 0 分享

CSS制作符合网站标准的细线表格

css教程:实现符合Web标准的细线表格   随着应用CSS网页布局构建网页,   以及web标准的广泛普及与发展,   表格渐渐被人们遗忘,   但是表格还是有它优秀的一面,   数据处理用表格的确省了不少麻烦!   这个是细表格的代码,并且通过了标准验证! tab
收藏 0 赞 0 分享

区分IE6,IE7和firefox的CSS hack

这篇文章主要为介绍了CSS小技巧之有效区分IE6,IE7,Firefox,需要的朋友可以参考下
收藏 0 赞 0 分享

CSS教程:用dl dt dd来制作列表

  今天有人发邮件问的一个问题,最开始的想法是用ul列表来实现;但是这样用出现两个比较麻烦的地方:   1、如果用UL还布局,右边一栏比较麻烦;   2、文字外边的边框自适应比较麻烦;   3、很可能要定死高度;   所以,细细地看一看这个布局,想一想还是用DL.DT.DD作
收藏 0 赞 0 分享

性感的CSS菜单(Menus)

当你需要一个简单易用的导航菜单得时候。CSSMenu是个不错的选择。相对于Flash/Javascript,他们小巧轻便,而且方便使用。当然,他们也能做出很多很漂亮的效果。 CssMenuExample 这里有几个专门收集CSS导航菜单的站点,其中有很多优秀的作品。也许能给
收藏 0 赞 0 分享
查看更多