flex布局是真的香!

一直以来,简单、高效、精准且兼容性强的布局能力都是css努力的方向,但在传统布局中,只有floatposition布局是可靠且跨浏览器兼容的技术。为进一步提高布局能力,css在2009年推出布局利器flex和后面推出的布局王者grid,其中,flex拥有强大的一维布局能力,而grid则拥有更强大的二维布局能力。今天,我们主要基于居中问题、空间分布问题,margin+flex布局和响应式布局讨论布局利器flex技术。

居中问题

传统布局技术中,水平垂直居需求可以针对不同的元素类型使用text-alignmarginpaddingposition等手段实现,但是比较麻烦的这些技术使用都有特定对象,比如text-alignline-height针对的是内联元素,而margin: auto针对的是块级元素。flex布局技术中,简化了应用场景,无论是什么类型的元素,只要简单的设置just-content: center就可实现水平居中,简单设置align-items就可实现垂直居中,两者一并设置就可实现水平垂直居中。我们来看一下几个例子:

首先块级元素可以很容易的实现水平垂直居中。

    <head>
        <style>
            .father {
                /* 容器中设置flex布局 */
                display: flex;
                /* 实现水平居中 */
                justify-content: center;
                /* 实现垂直居中 */
                align-items: center;
                width: 500px;
                height: 500px;
                background-color: antiquewhite;
            }

            .son {
                width: 100px;
                height: 100px;
                background-color: lightpink;
            }
        </style>
    </head>
    <body>
        <div class="father">
            <div class="son"></div>
        </div>
    </body>
复制代码

image-20211120201821031.png

然后,我们来看看内联元素,同一套代码实现水平垂直居中也是没问题的。

    <head>
        <style>
            .father {
                /* 容器中设置flex布局 */
                display: flex;
                /* 实现水平居中 */
                justify-content: center;
                /* 实现垂直居中 */
                align-items: center;
                width: 500px;
                height: 500px;
                background-color: antiquewhite;
            }
            .son {
                width: 100px;
                height: 100px;
                background-color: lightpink;
            }
        </style>
    </head>
    <body>
        <div class="father">
            <!-- 将子元素直接替换为内联元素 -->
            <span class="son"></span>
        </div>
    </body>
复制代码

image-20211120201821031.png

通常,内联元素无法通过设置widthheight属性来确定元素的大小,但是一旦设置了flex布局,则自动给内联元素添加了display: block,而且flex布局会让floatclearvertical-align属性失效。

值得我们注意的是,flex布局的影响仅仅只是针对容器的直接子元素,而非所有的后代。我们看看下面的例子

<head>
    <style>
        .father {
            display: flex;
            justify-content: center;
            align-items: center;
            width: 500px;
            height: 500px;
            background-color: antiquewhite;
        }
        .container {
            width: 200px;
            height: 200px;
            background-color: rgb(9, 131, 238);
        }
        .son {
            width: 100px;
            height: 100px;
            background-color: lightpink;
        }
    </style>
</head>
<body>
    <div class="father">
        <!-- 添加一个容器包裹son -->
        <div class="container">
            <span class="son">son</span>
        </div>
    </div>
</body>
复制代码

2.png

通过添加一层容器包裹住span元素,则无法将flex的影响传递至span元素,无法设置高宽,并且水平垂直设置无法通过container传递至非father的直接子元素span上。

空间分布问题

flex布局可以克服传统布局很难实现灵活空间分布和对齐的问题。在flex的主轴上(默认为水平x轴),可以通过设置flexjustify-content操控空间分配和对齐,在flex的交叉轴上(默认为垂直y轴),可以通过设置align-itemsalign-self操控空间分配和对齐。

1.主轴空间

首先,来看看在多列布局,设置各列固定宽度,均匀分布在水平轴上,实现如下效果:

3.png

传统布局要实现各列布局自适应父容器的水平空间均匀分布是很麻烦的,至少无法直接通过堆积float+margin实现。但是使用flex布局可以直接设置justify-content:space-evenly实现。

    <head>
        <style>
            .container {
                display: flex;
                width: 800px;
                height: 500px;
                background-color: antiquewhite;
                /* 设置justify-content可以均分空间 */
                justify-content: space-evenly;
            }
​
            .container > * {
                line-height: 50px;
                text-align: center;
                width: 50px;
                height: 50px;
                background-color: lightcoral;
            }  
        </style>
    </head>
    <body>
        <div class="container">
            <div class="item1">item1</div>
            <div class="item2">item2</div>
            <div class="item3">item3</div>
            <div class="item4">item4</div>
            <div class="item5">item5</div>
        </div>
    </body>
复制代码

再来看看元素空间分配,我们可以设置flex设置元素在主轴的空间分配,flex属性具体空间分配算法可以参考我的另外一篇文章Flex布局空间分配。假设要设置3列空间,两边为固定宽度的sidebar,中间为自适应宽度的content,效果如下:

1.gif

传统布局可以借助float或者position布局策略实现,但是都不够简洁,使用flex布局时,只要设置中间content区域为flex:1(主要是调节flex-grow属性),左右两边区域为定宽即可实现。

    <head>
        <style>
            .main {
                display: flex;
                height: 500px;
                background-color: antiquewhite;
            }
​
            .main > * {
                line-height: 50px;
                text-align: center;
            }  
​
            .sidebar-left {
                width: 100px;
                background-color: aqua;
            }
​
            .content {
                /* flex默认为0 1 auto;不具有只适应能力 */
                flex:1;
                background-color:coral;
            }
​
            .sidebar-right {
                width: 100px;
                background-color: aqua;
            }
​
        </style>
    </head>
    <body>
        <div class="main">
            <div class="sidebar-left">sidebar-left</div>
            <div class="content">content</div>
            <div class="sidebar-right">sidebar-right</div>
        </div>
    </body>
复制代码

bootstrap的布局的栅格系统Grid system也是利用flex布局,设置flex属性和width属性将主轴空间分为12份。在bootstrap的源码,首先row类中设置flex布局,然后在col-*col-md-*等布局属性中设置对应占比width以及flex属性实现不同的空间分配。

.row {
  --bs-gutter-x: 1.5rem;
  --bs-gutter-y: 0;
 /* 在row类中设置flex布局 */
  display: flex;
  flex-wrap: wrap;
  margin-top: calc(-1 * var(--bs-gutter-y));
  margin-right: calc(-0.5 * var(--bs-gutter-x));
  margin-left: calc(-0.5 * var(--bs-gutter-x));
}
​
.col {
   /* 在col类中设置flex为1,即初始宽度为0,所有列等分空间 */
  flex: 1 0 0%;
}
​
.col-1 {
  flex: 0 0 auto;
 /* 在col-1类中设置宽度为1/12 */
  width: 8.33333333%;
}
​
.col-2 {
  flex: 0 0 auto;
   /* 在col-1类中设置宽度为2/12 */
  width: 16.66666667%;
}
​
.col-3 {
  flex: 0 0 auto;
   /* 在col-1类中设置宽度为3/12 */
  width: 25%;
}
​
/* col-*后面就不写了 */
​
复制代码

于是,可以实现以下效果:

4.png

<div class="container">
  <div class="row">
    <div class="col">
      1 of 3
    </div>
    <div class="col-6">
      2 of 3 (wider)
    </div>
    <div class="col">
      3 of 3
    </div>
  </div>
  <div class="row">
    <div class="col">
      1 of 3
    </div>
    <div class="col-5">
      2 of 3 (wider)
    </div>
    <div class="col">
      3 of 3
    </div>
  </div>
</div>
复制代码

5.png

<div class="container">
  <div class="row">
    <div class="col">
      1 of 2
    </div>
    <div class="col">
      2 of 2
    </div>
  </div>
  <div class="row">
    <div class="col">
      1 of 3
    </div>
    <div class="col">
      2 of 3
    </div>
    <div class="col">
      3 of 3
    </div>
  </div>
</div>
复制代码

最后,我们还可以利用flex实现sticky footer效果,如下图所示,可以将footer直接推至页面底部,这种方案就很简洁。

8.png

<head>
    <style>
        html {
            height: 100%;
        }

        body {
            min-height: 100%;
            display: flex;
            /* 将主轴方向设置为y轴*/
            flex-direction: column;
        }
        
        header {
            height: 50px;
            background-color: aqua;
        }

        main {
            /* 这里将main在主轴方向上占用所有剩余空间*/
            flex: 1;
            background-color: aquamarine;
        }

        footer {
            background-color: antiquewhite;
            height: 50px;
        }
    </style>
</head>

<body>
    <header>header</header>
    <main>main</main>
    <footer>footer</footer>
</body>
复制代码

2.交叉轴空间

在交叉轴上,我们使用flex布局的align-itemsalign-self可以克服等高布局、底线对齐等问题,相当灵活,很是方便!

比如在第一小节居中问题上,我们利用align-items: center设置元素为垂直居中。比如在上一小节中的3列空间只适应布局,我们利用align-items的默认值stretch,实现sidebar-leftcontentsidebar-right三列空间等高布局。

对于底线对齐布局,我们也可以简洁的利用align-items: flex-end实现。

   <head>
       <style>
           .container {
               display: flex;
               height: 500px;
               width: 500px;
               background-color: antiquewhite;
               align-items: flex-end;
           }

           .container > * {
               text-align: center;
               width: 100px;
               margin: 0 30px;
               background-color: aquamarine;
           }  

           .item1 {
               height: 100px;
           }

           .item2 {
               height: 200px;
           }

           .item3 {
               height: 300px;
           }


       </style>
   </head>
   <body>
       <div class="container">
           <div class="item1">item1</div>
           <div class="item2">item2</div>
           <div class="item3">item3</div>
       </div>
   </body>
复制代码

6.png

margin+flex布局

如果说flex布局是利器的话,那margin: auto配合上flex布局那就是神器。

虽然flex布局很强,但是flex布局的空间分配策略主要集中在flex元素上,若是要控制元素彼此间的间距,那就弱很多,只有justify-content属性的几个关键字可以控制。比如实现以下两端导航栏就显得比较困难了。

7.png

这种情况当然也可以用float布局解决,也可以使用通过容器将前三个元素HOME,BLOG,ARTICLE包裹起来,然后在用justify-content: between实现。这里,我们使用margin:auto将更加高效。我们可以在最后一个元素中使用margin-left: auto。将最后一个元素推至最右边即可。

<head>
    <style>
        ul {
            border: 1px solid rgb(216, 216, 216);
            width: 80%;
            margin: auto;
            padding: 0;
            display: flex;
        }
​
        li {
            margin: 0;
            list-style: none;
            line-height: 3em;
            border-bottom: 1px solid transparent;
            padding: 0 1em;
        }
​
        li:hover {
            border-bottom: 1px solid rgb(8, 250, 0);
            cursor: pointer;
        }
​
        li a {
            text-decoration: none;
            color: black;
        }
​
        li:last-child {
            /* 最后一个元素使用margin-left: auto。将自身推至最右边 */
            margin-left: auto;
        }
​
    </style>
</head>
​
<body>
    <nav>
        <ul>
            <li><a href="#">HOME</a></li>
            <li><a href="#">BLOG</a></li>
            <li><a href="#">ARTICLE</a></li>
            <li><a href="#">CONTACT</a></li>
        </ul>
    </nav>
</body>
复制代码

如张鑫旭老师所言,margin:auto填充规则如下:

  1. 如果一侧定值,一侧auto,则auto为剩余空间大小
  2. 如果两侧均为auto,则平分剩余空间

我们在块级元素利用定宽+margin:auto实现水平居中,在绝对定位中设置定宽高且对立属性为0,然后利用margin:auto实现水平垂直居中也是利用上述的特性。而在flex布局中,除元素本身高宽外,其他空间都是剩余空间,即可利用margin:auto控制如何分配。

比如我们在flex布局中设置元素定宽,然后使用margin:auto实现水平居中。在flex布局中设置元素定高,然后使用margin:auto实现垂直居中,有兴趣的同学可以自行尝试。

响应式布局

借助flex布局技术,配合媒体查询技术可以很容易的实现响应式布局,就拿上述的导航栏例子说,我们添加下面的媒体查询代码:

        @media screen and (max-width:500px) {
            /* 自适应导航栏 */
            ul {
                flex-flow: column;
                
            }
​
            li:last-child {
                margin-left: 0;
            }
        }
复制代码

即可实现在屏幕宽度低于500px时,导航栏垂直布局的效果:

2.gif

总结

总的来说,flex在一维空间的控制能力是很强的,主要体现在主轴和交叉轴对齐和空间分配上操作十分简洁高效,可以很方便的实现水平垂直居中、空间等分、底部对齐、等高对齐,自适应多列布局和响应式布局,同时,margin: auto方案配合上flex布局更是如虎添翼,在空间分布的操控能力更上一层楼,基本可以满足日常布局需求了。当下,大多数浏览器都支持flex布局技术了,诸如 Firefox, Chrome, Opera, Microsoft Edge 和 IE 11,若flex满足项目兼容性需求,flex布局是一维布局的首选方案。

猜你喜欢

转载自juejin.im/post/7032686438919438349