Flex布局

47 阅读8分钟

介绍

传统的布局解决方案,主要是基于盒模型,然后依赖display、position、float来进行布局,对于一些比较特殊的布局并不方便,比如垂直水平居中

在2009年,W3C就推出了一种新的布局方案,也就是flex布局,可以简便、完整、响应式地实现各种页面布局。目前,它已经得到了所有浏览器的支持,这意味着,现在就能很安全地使用这项功能。

盒模型

盒模型主要由四个部分组成,由内而外分别为content、padding、border、margin

盒模型在分类上,有两种模式,分别是标准盒模型(默认)和IE盒模型,两者的区别在于如何计算元素的宽高

标准:content+padding+margin(向外扩)

IE:content(包含margin、padding),如果需要使用该盒模型,可以通过设置box-sizing:border-box属性来启用(向内缩)

布局原理

但是需要注意的是,设为Flex布局以后,子元素的float、clear和vertical-align(用来指定行内、行内区块的垂直对齐方式)属性将失效

flex布局也称为弹性布局,可以为盒模型提供最大的灵活性,任何一个容器都可以指定为flex布局

它的核心原理是基于容器和项目的关系,通过定义主轴和交叉轴来实现灵活的布局。

1、容器的属性

  1. flex-direction:row(默认)、row-reverse、column、column-reverse,确定项目主轴方向

  1. flex-wrap:nowrap(默认)、wrap、wrap-reverse,换行属性

  1. flex-flow:该属性是1和2的简写形式,默认值为row nowrap
  2. justify-content:flex-start(默认)、flex-end、center、space-between、space-around(每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍)、space-evenly(每个项目两边的距离相等)、stretch(进行均分,空余空间在盒子左侧),主轴对齐方式

  1. align-items:flex-start、flex-end、center、baseline(项目的第一行文字的基线对齐)、stretch(默认,没有设置高度或者设置为auto,将撑满容器),交叉轴的对齐方式

  1. align-content:flex-start、flex-end、center、space-between、space-around、stretch(默认值),定义了多根交叉轴轴线的对齐方式

align-items与align-content的区别:align-items 控制单行内项目的对齐,而 align-content 控制多行之间的对齐

只有当容器内的项目出现多行/多列时,align-content 才会生效

在单行布局中,align-content 属性不起作用

2、项目的属性

  1. order:定义项目的排列顺序。数值越小,排列越靠前,默认为0。
  2. flex-grow:定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。

  1. flex-shrink:定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。

  1. flex-basis:定义了在分配多余空间之前,项目占据的主轴空间,浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。它可以设为跟width或height属性一样的值,则项目将占据固定空间。

在使用该属性时候,会出现优先级问题,优先级如下:max-width > flex-basis > width

  1. flex:为2、3、4三个属性的简写,默认值为0 1 auto,后两个属性是可选属性。在开发的过程中,通常会使用flex属性,而不会去单独设置。

该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。

根据属性可以得知,前者表示表示项目可以根据可用空间放大或缩小,初始大小由内容决定,后者表示表示项目不会放大或缩小,保持其初始大小。

想要具体了解flex属性的,可以参考这篇文章:juejin.cn/post/747559…

  1. align-self:auto(默认值)、flex-start、flex-end、center、baseline、stretch,允许单个项目有与其他项目不一样的对齐方式,可以覆盖align-items属性。

继承父元素的align-items属性,如果没有父元素,则等同于stretch。

优势

  1. 简化布局代码:通过简单的 CSS 属性即可实现复杂的布局效果。
  2. 灵活的排列方式:支持水平或垂直排列,并可以轻松切换方向。
  3. 响应式设计:项目的大小可以动态调整,适应不同的屏幕尺寸。
  4. 对齐和分布空间:提供多种对齐和分布空间的方式,如居中、均匀分布等。

常见使用场景

  1. 导航栏
  2. 居中实现
  3. 卡片布局
  4. 撑满剩余空间

代码

完整的代码都在下方了,如果需要的话,可以复制下来自己感受一下

import React from "react";
import "./index.less";

const Index = () => {
    return (
        <>
            <section>
                <h1>0、盒模型</h1>
                <div className="card">
                    <div className="item1"></div>
                    <div className="item2"></div>
                </div>
            </section>

            <section>
                <h1>1、flex-direction、flex-warp:确定项目主轴方向以及换行</h1>
                <div className="flex-container1">
                    <div className="item">1</div>
                    <div className="item">2</div>
                    <div className="item">3</div>
                    <div className="item">4</div>
                    <div className="item">5</div>
                </div>
            </section>

            <section>
                <h1>2、justify-content:设置主轴的对齐方式</h1>
                <div className="flex-container2">
                    <div className="item">1</div>
                    <div className="item">2</div>
                    <div className="item">3</div>
                    <div className="item">4</div>
                    <div className="item">5</div>
                </div>
            </section>

            <section>
                <h1>3、align-items:设置交叉轴的对齐方式</h1>
                <div className="flex-container3">
                    <div className="item">
                        <h1>1</h1>
                    </div>
                    <div className="item">2</div>
                    <div className="item">3</div>
                    <div className="item">4</div>
                    <div className="item">5</div>
                </div>
            </section>

            <section>
                <h1>4、align-content:定义了多根交叉轴轴线的对齐方式</h1>
                <div className="flex-container4">
                    <div className="item">1</div>
                    <div className="item">2</div>
                    <div className="item">3</div>
                    <div className="item">4</div>
                    <div className="item">5</div>
                    <div className="item">6</div>
                    <div className="item">7</div>
                    <div className="item">8</div>
                    <div className="item">9</div>
                </div>
            </section>

            <section>
                <h1>5、order:定义项目的排列顺序</h1>
                <div className="flex-container5">
                    <div className="item item1">order为1</div>
                    <div className="item">2</div>
                    <div className="item">3</div>
                    <div className="item">4</div>
                    <div className="item">5</div>
                </div>
            </section>

            <section>
                <h1>
                    6、flex-grow:默认为0,均分剩余的空间。计算规则:将剩余空间除以所有flex-grow属性之和,然后按照比例分给带有该属性的盒子
                </h1>
                <div className="flex-container6">
                    <div className="item1">100px,flex-grow:1</div>
                    <div className="item2">100px,flex-grow:1</div>
                    <div className="item3">100px,flex-grow:3</div>
                </div>
            </section>

            <section>
                <h1>
                    7、flex-shrink:默认为1。计算规则:将不足的空间除以带有flex-shrink的盒子,然后再进行对应的缩小比例,需要注意的是,该属性默认为1,如果不进行设置,那么默认所有的盒子参与缩小。
                </h1>
                <div className="flex-container7">
                    <div className="item1">200px 3</div>
                    <div className="item2">100px 1</div>
                    <div className="item3">100px 1</div>
                </div>
            </section>

            <section>
                <h1>8、flex-basis:默认为auto。计算规则:默认就是项目本身的大小</h1>
                <div className="flex-container8">
                    <div className="item1">100px 250px</div>
                    <div className="item2">100px auto</div>
                    <div className="item3">100px auto</div>
                </div>
            </section>

            <section>
                <h1>9、flex</h1>
                <div className="flex-container9">
                    <div className="item1">400px </div>
                    <div className="item2">400px </div>
                    <div className="item3">400px </div>
                </div>
            </section>

            <section>
                <h1>
                    10、align-self:默认为auto,允许单个项目有与其他项目不一样的对齐方式,可以覆盖align-items属性
                </h1>
                <div className="flex-container10">
                    <div className="item1">1</div>
                    <div className="item2">2</div>
                    <div className="item3">3</div>
                </div>
            </section>

            <section>
                <h1>常见使用场景</h1>
                <h2>导航栏示例</h2>
                <section className="demo-section">
                    <nav className="demo-nav">
                        <a href="#">首页</a>
                        <a href="#">产品</a>
                        <a href="#">服务</a>
                        <a href="#">关于</a>
                    </nav>
                </section>

                <h2>居中实现示例</h2>
                <section className="demo-section">
                    <div className="center-container">
                        <div className="center-item">完美居中</div>
                    </div>
                </section>

                <h2>卡片布局示例</h2>
                <section className="demo-section">
                    <div className="card-list">
                        <div className="cards">卡片1</div>
                        <div className="cards">卡片2</div>
                        <div className="cards">卡片3</div>
                    </div>
                </section>

                <h2>撑满剩余空间示例</h2>
                <section className="demo-section">
                    <div className="flex-space">
                        <div className="fixed-width">固定宽度</div>
                        <div className="flex-grow">自动撑满剩余空间</div>
                    </div>
                </section>
            </section>
        </>
    );
};

export default Index;
body {
    padding: 20px;
}

.card {
    margin-bottom: 20px;

    .item1 {
        width: 100px;
        height: 100px;
        background-color: #ccc;
        margin: 10px;
        padding: 10px;
    }

    .item2 {
        width: 100px;
        height: 100px;
        background-color: #ccc;
        margin: 10px;
        padding: 10px;
        box-sizing: border-box;
    }
}

.flex-container1 {
    display: flex;
    border: 1px solid red;
    width: 400px;
    flex-direction: row;
    flex-wrap: nowrap;
    margin-bottom: 20px;

    .item {
        width: 100px;
        height: 100px;
    }
}

.flex-container2 {
    display: flex;
    border: 1px solid red;
    width: 1000px;
    justify-content: flex-start;
    margin-bottom: 20px;

    .item {
        width: 100px;
        height: 100px;
    }

}

.flex-container3 {
    display: flex;
    border: 1px solid red;
    width: 1000px;
    flex-wrap: nowrap;
    align-items: stretch;
    height: 100px;
    margin-bottom: 20px;

    .item {
        width: 100px;
    }
}

.flex-container4 {
    display: flex;
    border: 1px solid red;
    width: 300px;
    height: 400px;
    flex-wrap: wrap;
    align-content: center; // 用来设置行的对齐方式

    .item {
        width: 100px;
        height: 100px;
        background-color: #f0f0f0;
        display: flex;
        justify-content: center;
        align-items: center;
    }
}

.flex-container5 {
    display: flex;
    border: 1px solid red;
    width: 1000px;
    flex-wrap: nowrap;
    align-items: stretch;
    height: 100px;
    margin-bottom: 20px;

    .item {
        width: 100px;
    }

    .item1 {
        order: 1;
    }
}

.flex-container6 {
    display: flex;
    border: 1px solid red;
    width: 500px;
    height: 100px;
    margin-bottom: 20px;

    .item1 {
        width: 100px;
        background-color: lightcoral;
        flex-grow: 1;
    }

    .item2 {
        width: 100px;
        background-color: lightgreen;
        flex-grow: 1;

    }

    .item3 {
        width: 100px;
        background-color: lightblue;
        flex-grow: 3;
    }
}

.flex-container7 {
    display: flex;
    border: 1px solid red;
    width: 300px;
    height: 100px;
    margin-bottom: 20px;

    .item1 {
        width: 200px;
        background-color: lightcoral;
        // flex-shrink: 0; // 不参与压缩
        // flex-shrink: 3; // 压缩比重高
        min-width: 150px;
    }

    .item2 {
        width: 100px;
        background-color: lightgreen;

    }

    .item3 {
        width: 100px;
        background-color: lightblue;
    }
}

.flex-container8 {
    display: flex;
    border: 1px solid red;
    width: 500px;
    height: 100px;
    margin-bottom: 20px;

    .item1 {
        width: 100px;
        background-color: lightcoral;
        // flex-basis: auto;
        // flex-basis: 250px; // 不设置就是原本的值
        // max-width: 200px; // 优先级最高
    }

    .item2 {
        width: 100px;
        background-color: lightgreen;

    }

    .item3 {
        width: 100px;
        background-color: lightblue;
    }
}

.flex-container9 {
    display: flex;
    border: 1px solid red;
    width: 1000px;
    height: 100px;
    margin-bottom: 20px;
    
    .item1 {
        width: 400px;
        flex: none;
        background-color: lightcoral;
    }

    .item2 {
        width: 400px;
        flex: none;

        background-color: lightgreen;

    }

    .item3 {
        width: 400px;
        flex: none;
        background-color: lightblue;
    }
}

.flex-container10 {
    display: flex;
    border: 1px solid red;
    width: 500px;
    height: 200px;
    margin-bottom: 20px;
    align-items: center;

    .item1 {
        width: 100px;
        height: 100px;
        background-color: lightcoral;
        align-self: start; // 单独设置,可以有不同的排列方式
    }

    .item2 {
        width: 100px;
        height: 100px;
        background-color: lightgreen;

    }

    .item3 {
        width: 100px;
        height: 100px;
        background-color: lightblue;
    }
}

// 导航栏示例
.demo-section {
    border: 1px solid #000000;
    margin-bottom: 20px;

    .demo-nav {
        display: flex;
        background-color: #333;
        padding: 15px;

    }

    .demo-nav a {
        color: white;
        text-decoration: none;
        padding: 10px 20px;
    }

}

// 居中实现示例
.center-container {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 200px;
    background-color: #f0f0f0;

    .center-item {
        padding: 20px;
        background-color: #333;
        color: white;
    }
}

// 卡片布局示例
.card-list {
    display: flex;
    justify-content: space-between;

    .cards {
        flex: 0 1 calc(33.333% - 20px);
        min-width: 100px;
        height: 100px;
        background-color: #f0f0f0;
        display: flex;
        justify-content: center;
        align-items: center;
    }
}

// 撑满剩余空间示例
.flex-space {
    display: flex;
    gap: 20px;

    .fixed-width {
        width: 200px;
        padding: 20px;
        background-color: #f0f0f0;
    }

    .flex-grow {
        flex: 1;
        padding: 20px;
        background-color: #f0f0f0;
    }
}