验证 flex-grow 与 flex-shrink 的计算公式

背景简介

关于 flex-grow 与 flex-shrink 的计算公式,网上已经有很多详细的分析文章了,这里不再阐述原理,只用一些简单的 js 函数来模拟计算过程。

这里放几篇介绍原理的文章:

  1. https://blog.csdn.net/Snoopyqiuer/article/details/102472358
  2. https://juejin.cn/post/6844904016439148551#heading-5
  3. https://juejin.cn/post/6844904153634832392

概括

与 grow 和 shrink 计算相关的属性有:

  • 父元素和子元素都要考虑:marigin/padding/border/box-sizing,
  • 仅子元素需要考虑:flex-basis(会覆盖子项的 width),以及 max-width(仅针对 grow),min-wdith(仅针对 shrink)

代码示例

本例仅计算以子项的 width 和对应的 grow/shrink 为依据的实际宽度,并未考虑 marigin/padding/border/box-sizing/flex-basis/max-width/min-wdith 等属性。

其中,grow 之和 > 1 和 shrink 之和 > 1 分组的中间一项,grow 和 shrink 分别是 0;其他子项都设置了非 0 值。

flex-shrink

HTML:

<!-- grow 之和 > 1 -->
<div class="container grow grow1">
    <div class="content">哈哈哈哈哈哈哈</div>
    <div class="content">哈哈哈哈哈哈哈</div>
    <div class="content">哈哈哈哈哈哈哈</div>
</div>
<!-- grow 之和 = 1 -->
<div class="container grow grow2">
    <div class="content">哈哈哈哈哈哈哈</div>
    <div class="content">哈哈哈哈哈哈哈</div>
    <div class="content">哈哈哈哈哈哈哈</div>
</div>
<!-- grow 之和 < 1 -->
<div class="container grow grow3">
    <div class="content">哈哈哈哈哈哈哈</div>
    <div class="content">哈哈哈哈哈哈哈</div>
    <div class="content">哈哈哈哈哈哈哈</div>
</div>
<!-- shrink 之和 > 1 -->
<div class="container shrink shrink1">
    <div class="content">哈哈哈哈哈哈哈哈哈哈或或或或或或或或</div>
    <div class="content">哈哈哈哈哈哈哈哈哈哈或或或或或或或或</div>
    <div class="content">哈哈哈哈哈哈哈哈哈哈或或或或或或或或</div>
</div>
<!-- shrink 之和 = 1 -->
<div class="container shrink shrink2">
    <div class="content">哈哈哈哈哈哈哈哈哈哈或或或或或或或或</div>
    <div class="content">哈哈哈哈哈哈哈哈哈哈或或或或或或或或</div>
    <div class="content">哈哈哈哈哈哈哈哈哈哈或或或或或或或或</div>
</div>
<!-- shrink 之和 < 1 -->
<div class="container shrink shrink3">
    <div class="content">哈哈哈哈哈哈哈哈哈哈或或或或或或或或</div>
    <div class="content">哈哈哈哈哈哈哈哈哈哈或或或或或或或或</div>
    <div class="content">哈哈哈哈哈哈哈哈哈哈或或或或或或或或</div>
</div>

CSSS:

展开查看

* {
    margin: 0;
    padding: 0;
}

.container {
display: flex;
flex-flow: no-wrap row;
margin-bottom: 40px;
width: 400px;
}

.content {
width: 400px;
}

.grow {
max-width: 450px;
}

/* grow 之和 > 1 */
.grow1>.content:nth-child(1) {
flex-grow: 2;
width: 120px;
}

.grow1>.content:nth-child(2) {
flex-grow: 0;
width: 80px;
}

.grow1>.content:nth-child(3) {
flex-grow: 3;
width: 160px;
}

/* grow 之和 = 1 */
.grow2>.content:nth-child(1) {
flex-grow: 0.2;
width: 120px;
}

.grow2>.content:nth-child(2) {
flex-grow: 0.5;
width: 80px;
}

.grow2>.content:nth-child(3) {
flex-grow: 0.3;
width: 160px;
}

/* grow 之和 < 1 */
.grow3>.content:nth-child(1) {
flex-grow: 0.2;
width: 120px;
}

.grow3>.content:nth-child(2) {
flex-grow: 0.1;
width: 80px;
}

.grow3>.content:nth-child(3) {
flex-grow: 0.3;
width: 160px;
}

/* shrink 之和 > 1 */
.shrink1>.content:nth-child(1) {
width: 120px;
flex-shrink: 2;
}

.shrink1>.content:nth-child(2) {
width: 150px;
flex-shrink: 0;
}

.shrink1>.content:nth-child(3) {
width: 200px;
flex-shrink: 3;
}

/* shrink 之和 = 1 */
.shrink2>.content:nth-child(1) {
width: 120px;
flex-shrink: 0.2;
}

.shrink2>.content:nth-child(2) {
width: 150px;
flex-shrink: 0.5;
}

.shrink2>.content:nth-child(3) {
width: 200px;
flex-shrink: 0.3;
}

/* shrink 之和 < 1 */
.shrink3>.content:nth-child(1) {
width: 120px;
flex-shrink: 0.2;
}

.shrink3>.content:nth-child(2) {
width: 150px;
flex-shrink: 0.1;
}

.shrink3>.content:nth-child(3) {
width: 200px;
flex-shrink: 0.3;
}

Javascript:

展开查看

// 弹性盒子子元素
class FlexItemElement {
    width = 0;
    grow = 0;
    shrink = 1;
    constructor(width = 0, grow = 0, shrink = 1) {
        this.width = width;
        this.grow = grow;
        this.shrink = shrink;
    }
}

// 计算子元素总权重,总收缩比与总宽度
function getParams(elements, flexType) {
let totalChildWidth = 0,
totalWeight = 0, // 元素的 width * grow/shrink 之和
totalFlex = 0; // 元素的 grow 或 shrink 之和
if (flexType) {
elements.forEach(element => {
let { [flexType]: elementFlex, width } = element;
totalWeight += elementFlex * width;
totalFlex += elementFlex;
});
} else {
elements.forEach(element => {
totalChildWidth += element.width;
});
}
return {
totalChildWidth, totalWeight, totalFlex
}
}
// 计算子元素 shrink 后的实际宽度
// flexType 用于指定 grow 或 shrink
function getFlexWidth(elements, parentWidth) {
if (elements.length > 0) {
// 判定当前需要 grow 还是 shrink
let flexType = '';
let { totalChildWidth } = getParams(elements);
let gap = parentWidth - totalChildWidth;
flexType = gap > 0 ? 'grow' : gap < 0 ? 'shrink' : '';

    let { totalWeight, totalFlex } = getParams(elements, flexType);
    let results = [];

    gap = Math.abs(gap);
    // grow 或 shrink 之和小于1,则只取差值的一部分
    if (totalFlex < 1) {
        gap *= totalFlex;
    }
    elements.forEach(element => {
        let flexWidth = element.width;
        if (element[flexType] !== 0) { // grow 或 shrink 不为0
            switch (flexType) {
                case 'grow': flexWidth = element.width + gap * element[flexType] / totalFlex; break;
                case 'shrink': flexWidth = element.width - gap * element.width * element[flexType] / totalWeight; break;
                default: break; // 不需要调整
            }
        }
        results.push(+(flexWidth).toFixed(2));
    });
    console.log(`totalFlex: ${totalFlex}`);
    return results
}

}

// main
let parentWidth = 400;
// grow test
let growElements1 = [new FlexItemElement(120, 2), new FlexItemElement(80), new FlexItemElement(160, 3)];
let growElements2 = [new FlexItemElement(120, 0.2), new FlexItemElement(80, 0.5), new FlexItemElement(160, 0.3)];
let growElements3 = [new FlexItemElement(120, 0.2), new FlexItemElement(80, 0.1), new FlexItemElement(160, 0.3)];
console.log(getFlexWidth(growElements1, parentWidth));
console.log(getFlexWidth(growElements2, parentWidth));
console.log(getFlexWidth(growElements3, parentWidth));

// shrink test
let shrinkElements1 = [new FlexItemElement(120, 0, 2), new FlexItemElement(150, 0, 0), new FlexItemElement(200, 0, 3)];
let shrinkElements2 = [new FlexItemElement(120, 0, 0.2), new FlexItemElement(150, 0, 0.5), new FlexItemElement(200, 0, 0.3)];
let shrinkElements3 = [new FlexItemElement(120, 0, 0.2), new FlexItemElement(150, 0, 0.1), new FlexItemElement(200, 0, 0.3)];
console.log(getFlexWidth(shrinkElements1, parentWidth));
console.log(getFlexWidth(shrinkElements2, parentWidth));
console.log(getFlexWidth(shrinkElements3, parentWidth));

posted @ 2022-07-08 16:32  CJc_3103  阅读(44)  评论(0编辑  收藏  举报