Skip to content

本文由 简悦 SimpRead 转码, 原文地址 mp.weixin.qq.com

随着 Web 技术不断的革新,CSS 近几年也变得多年前要更强大。时至今日,其中有一些 CSS 的属性可以让开发者能节约更多的时间。比如说,在 Web 布局中,现代 CSS 特性就可以更好的帮助我们快速实现,例如等高布局,水平垂直居中,经典的圣杯布局、宽高比例、页脚保持在底部等。在本文中,我将会介绍一些不同的 CSS 属性来实现这些效果,希望大家会感兴趣❞

「水平垂直居中」

「如何实现水平垂直居中」 可以说是 CSS 面试题中的经典面试题,在多年前这个面试题给很多同学都带来了困惑,但 「Flexbxo 布局模块」「CSS Grid 布局模块」 的到来,可以说实现水平垂直居中已是非常的容易。

「Flexbox 中实现水平垂直居中」

在 Flexbox 布局模块中,不管是单行还是多行,要让它们在容器中水平垂直居中都是件易事,而且方法也有多种。最常见的是在 Flex 容器上设置对齐方式,在 Flex 项目上设置margin:auto

先来看在 Flex 容器上设置对齐方式。

「Flex 容器和 Flex 项目上设置对齐方式」

从《Flexbox 布局中的对齐方式》(https://juejin.cn/book/7161370789680250917/section/7161623670622781471)一文中,我们可以知道在 Flex 容器上设置justify-contentalign-items的值为center时,可以让元素在 Flex 容器中达到水平垂直居中的效果。来看一个示例:

<!-- HTML -->
<div class="flex__container">
    <div class="flex__item"></div>
</div>
/* CSS */
.flex__container {
    display: flex;
    justify-content: center;
    align-items: center;
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/YzwYRRy

这种方式特别适应于让 Icon 图标在容器中水平垂直居中,不同的是在 Icon 图标容器上显示设置display: inline-flex。比如下面这个示例:

<!-- HTML -->
<div class="flex__container">
    <svg> </svg>
</div>
/* CSS */
.flex__container {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/xxZpQNv

在这种模式之下,如果要让多个元素实现水平垂直居中的效果,那还需要加上flex-direction: column,比如:

<!-- HTML -->
<div class="flex__container">
    <div class="avatar">:)</div>
    <div class="media__heading"></div>
    <div class="media__content"></div>
    <div class="action"></div>
</div>
/* CSS */
.flex__container  {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/QWyazpZ

在 Flexbox 布局中,还可以像下面这样让 Flex 项目在 Flex 容器中达到水平垂直居中的效果:

<!-- HTML -->
<div class="flex__container">
    <div class="flex__item"></div>
</div>
/* CSS */
.flex__container {
    display: flex; // 或inline-flex
    justify-content: center;
}

.flex__item {
    align-self: center;
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/yLepGKW

如果在 Flex 容器中有多个 Flex 项目时,该方法同样有效:

.flex__container {
    display: flex; // 或inline-flex
    justify-content: center;
}

.flex__container > * {
    align-self: center;
}

比如下面这个效果:

Demo 地址:https://codepen.io/airen/pen/bGEaOjm

除此之外,还可以使用place-content: center让 Flex 项目实现水平垂直居中:

.flex__container {
    display: flex;
    place-content: center;
}

.flex__item {
    align-self: center;
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/gOPoZQz

或者换:

.flex__container {
    display: flex;
    place-content: center;
    place-items: center;
}

Demo 地址:https://codepen.io/airen/pen/JjGMwzE

这两种方式同样适用于 Flex 容器中有多个 Flex 项目的情景:

.flex__container {
    display: flex;
    flex-direction: column;
    place-content: center;
}

.flex__container > * {
    align-self: center;
}

// 或

.flex__container {
    display: flex;
    flex-direction: column;
    place-content: center;
    place-items: center;
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/XWXVoLd

可能很多同学对于place-contentplace-items会感到陌生。其实place-contentalign-contentjustify-content的简写属性;而place-itemsalign-itemsjustify-items的简写属性。即:

.flex__container {
    place-content: center;
    place-items: center;
}

等效于:

.flex__container {
    align-content: center;
    justify-content: center;

    align-items: center;
    justify-items: center;
}

虽然扩展出来有四个属性,但最终等效于:

.flex__container {
    display: flex;
    align-items: center;
    justify-content: center;
}

// 多行
.flex__container {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

「在 Flex 项目上设置」 margin: auto

如果在 Flex 容器中只有一个 Flex 项目,还可以显式在 Flex 项目中显式设置margin的值为auto,这样也可以让 Flex 项目在 Flex 容器中水平垂直居中。例如:

.flex__container {
    display: flex; // 或 inline-flex
}

.flex__item {
    margin: auto;
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/KKVZJNp

整个过程,你可以通过下面这个示例来体验。尝试着选中不同方向的margin值:

Demo 地址:https://codepen.io/airen/pen/gOPoqRq

「Grid 中实现水平垂直居中」

CSS Grid 布局可以说是现代 Web 布局中的银弹。它也是到目前为止布局系统中唯一一个二维布局系统。

在 CSS Grid 布局中,只需要仅仅的几行代码也可以快速的帮助我们实现水平垂直居中的效果。比如下面这个示例:

<!-- HTML -->
<div class="grid__container">
    <div class="grid__item"></div>
</div>
/* CSS */
.grid {
    display: grid; // 或 inline-grid
    place-items: center
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/zYrRYxW

在 CSS Grid 布局模块中,只要显式设置了display: grid(或inline-grid)就会创建 Grid 容器和 Grid 项目,也会自动生成网格线,即行和列(默认为一行一列)。

在没有显式地在 Grid 容器上设置grid-template-columnsgrid-template-rows,浏览器会将 Grid 容器默认设置为 Grid 内容大小:

这种方法也适用于 CSS Grid 容器中有多个子元素(Grid 项目),比如:

<!-- HTML -->
<div class="grid__container">
    <div class="avatar">:)</div>
    <div class="media__heading"></div>
    <div class="media__content"></div>
    <div class="action"></div>
</div>

这个时候你看到的效果如下:

Demo 地址:https://codepen.io/airen/pen/PoZQoGP

而且palce-items适用于每个单元格。这意味着它将居中单元格的内容。比如下面这个示例:

<!-- HTML -->
<div class="grid__container">
    <div class="grid__item">
        <h3>Special title treatment</h3>
        <p>With supporting text below as a natural lead-in to additional content.</p>
        <div class="action">Go somewhere</div>
    </div>
</div>
/* CSS */
.grid__container {
    display: grid;
    place-items: center;
    grid-template-columns: repeat(2, 1fr);
    gap: 2vh;
}
.grid__item {
    display: grid;
    place-items: center;
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/mdVXdpe

有关于这方面更详细的介绍,请移步阅读:

  • 使用 Flexbox 构建经典布局:10 种经典 Web 布局

  • 使用 Grid 构建经典布局:10 种经典布局

「等高布局」

等高布局也是 Web 中非常常见的一种布局方式,而且实现等高布局的方案也有很多种。这里我们主要来看 Flexbox 布局模块和 Grid 布局模块给我们带来了什么样的变化。

在 Flexbox 和 Grid 布局模块中,让我们实现等高布局已经是非常的简单了,比如:

<!-- Flexbox -->
<flex__container>
    <flex__item></flex__item>
    <flex__item></flex__item>
    <flex__item></flex__item>
</flex__container>
/* CSS */
.flex__container {
    display: flex; // 或 inline-flex
}

简单地说,在容器上显式设置了display的值为flexinline-flex,该容器的所有子元素的高度都相等,因为容器的align-items的默认值为stretch

这个时候你看到的效果如下:

Demo 地址:https://codepen.io/airen/pen/NWxyOQq

这种方式特别适用于卡片组件中:

Demo 地址:https://codepen.io/airen/pen/OJMQoOO

在 Grid 布局模块中类似:

<!-- HTML -->
<grid__container>
    <grid__item></grid__item>
    <grid__item></grid__item>
    <grid__item></grid__item>
</grid__container>
/* CSS * /
.grid__container {
 display : grid;
    grid-template-columns: 20vw 1fr 20vw; /* 根据需求调整值*/
}

如果需求有所调整,比如在**「Flex 项目」** 或 **「Grid 项目」**的子元素高度和容器高度相同。

<!-- HTML -->
<flex__container>
    <flex__item>
        <content></content>
    </flex__item>
</flex__container>
/* CSS */
.flex__container {
    display: flex;
}

.content {
    height: 100%
}

// 或
.grid__container {
    display: grid;
    grid-auto-flow: column;
}

.content {
    height: 100%;
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/jOWZdbo

有关于这方面更详细的介绍,请移步阅读:

  • 使用 Flexbox 构建经典布局:10 种经典 Web 布局

  • 使用 Grid 构建经典布局:10 种经典布局

  • 网格布局中的子网格和嵌套网格

「Sticky Footer」

首先用下图来描述什么是**「Sticky Footer」**布局效果:

Sticky Footer 实现方案和等高、垂直居中一样,同样**「有很多种方案可以实现」**。

比如像下面这样的结构:

<!-- HTML -->
<header></header>
<main></main>
<footer></footer>

先来看 Flexbox 布局模块中的实现方案:

body {
    display: flex;
    flex-direction: column;
}

footer {
    margin-top: auto;
}

Demo 地址:https://codepen.io/airen/pen/bGELzYy

可以尝试着在main区域右下角向下拖动,改变主内容区域的高度,你会发现 “当内容不足一屏时,<footer>会在页面的最底部,当内容超出一屏时,<footer>会自动往后延后”。

在 Flexbox 布局中,还可以在<main>区域上设置下面的样式,达到相等的效果:

body {
    display: flex;
    flex-direction: column;
}

main {
    flex: 1 0 auto;
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/zYrRXmY

<main>中的flex: 1 0 auto相当于是:

main {
    flex-grow: 1; /*容器有剩余空间时,main区域会扩展*/
    flex-shrink: 0; /*容器有不足空间时,main区域不会收缩*/
    flex-basis: auto; /*main区域高度的基准值为main内容自动高度*/
}

有关于这方面更详细的介绍,可以阅读《图解 CSS:Flexbox 布局 (Part2) 一文。如果你想省事的话,可以在main上显式设置flex-grow:1,因为flex-shrinkflex-basis的默认值为1auto

在 CSS Grid 布局中我们可以借助1fr<main>区域根据 Grid 容器剩余空间来做计算。

.grid__container {
    display: grid;
    grid-template-rows: auto 1fr auto;
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/MWKQRxd

有关于这方面更详细的介绍,请移步阅读:

  • 使用 Flexbox 构建经典布局:10 种经典 Web 布局

  • 使用 Grid 构建经典布局:10 种经典布局

「均分列」

在 Web 布局中,很多时候会对列做均分布局,最为常见的就是在移动端的底部 Bar,比如下图这样的一个效果:

在 Flexbox 和 Grid 还没出现之前,如果希望真正的做到均分效果,可以用100%(或100vw)除以具体的列数。比如:

<!-- HTML -->
<container>
    <column></column>
    <column></column>
    <column></column>
</container>
/* CCSS */
.container {
    inline-size: 50vw;
    min-inline-size: 320px;
    display: flex-row;
}

.column {
    float: left;
    width: calc(100% / 3);
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/LYGQoxL

通过浏览器调试器中可以发现,现个列的宽度都是相等的:

在 Flexbox 和 Grid 布局中,实现上面的效果会变得更容易地多。先来看 Flexbox 中的布局:

<!-- HTML -->
<flex__container>
    <flex__item></flex__item>
    <flex__item></flex__item>
    <flex__item></flex__item>
</flex__container>
/* CSS */
.flex__container {
    inline-size: 50vw;
    display: flex;
}

.flex__item {
    flex: 1;
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/yLevWEe

在 Flexbox 布局模块中,当flex取的值是一个单值(无单位的数),比如示例中的flex:1,它会当作显式的设置了flex-grow: 1。浏览器计算出来的flex

接下来看 Grid 中如何实现上例的效果

<!-- HTML -->
<grid__container>
    <grid__item></grid__item>
    <grid__item></grid__item>
    <grid__item></grid__item>
</grid__container>
/* CSS */
.grid__container {
    display: grid;
    grid-template-columns: repeat(3, 1fr); /*这里的3表示具体的列数*/
}

Demo 地址:https://codepen.io/airen/pen/NWxyVQP

这样的布局方式也适用于其他的布局中。但不管是 Flexbox 还是 Grid 布局中,都存在一定的缺陷,当容器没有足够的空间容纳 Flex 项目(或 Grid 项目)时,Flex 项目或 Grid 项目会溢出(或隐藏,如果 Flex 容器或 Grid 容器显式设置了overflow:hidden):

修复这种现象最简单的方式是在 Flex 容器或 Grid 容器显式设置一个min-width(或min-inline-size):

.flex__container {
    min-inline-size: 300px;
}

不过话又说回来,比如我们的 Flex 项目(或 Grid 项目)是一个卡片,每张卡片宽度是相等之外,更希望容器没有足够空间时,Flex 项目(或 Grid 项目)会自动断行排列。

我们继续通过示例向大家展示。先来看 Flexbox 实现方案:

.flex__container {
    display: flex;
    flex-wrap: wrap;
}

.flex__item {
    flex: 0 1 calc((100vw - 18vh) / 4); /* calc(100vw -18vh) / 4 是flex-basis的基准值 */
}

Demo 地址:https://codepen.io/airen/pen/dyGdBpw

你可以尝试着调整浏览器的视窗宽度,当浏览器的视窗越来越小时,Flex 容器宽度也就会越来越小,当 Flex 容器小到没有足够的空间容纳四个 Flex 项目(就此例而言),那么 Flex 项目就会断行排列:

基于该例,如果把 Flex 项目的flex值改成:

.flex__item {
    flex: 0 0 400px;
}

这个时候,当 Flex 容器没有足够空间时,Flex 项目会按flex-basis: 400px计算其宽度,Flex 容器没有足够空间时,Flex 就会断行:

反过来,如果 Flex 项目的值flex改成:

.flex__item {
    flex: 1 0 400px;
}

当 Flex 容器没有足够空间排列 Flex 项目时,Flex 项目会按flex-basis: 400px计算其宽度,Flex 会断行,并且同一行出现剩余空间时,Flex 项目会扩展,占满整个 Flex 容器:

在 Grid 中实现类似的效果要更复杂一点。可以使用repeat()函数,1fr以及auto-fit等特性:

.grid__container {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 2vh;
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/RwrQzeN

如果你对这方面知识感兴趣的话,还可以移步阅读《Container Query Solutions with CSS Grid and Flexbox》一文。

其实在 Grid 中与auto-fit对比的值还有一个叫auto-fill。但两者的差异是非常地大,用下图来描述auto-fitauto-fill的差异:

另外这种方式也是到目前为止一种不需要借助 CSS 媒体查询就可以实现响应式布局效果。具体的可以点击这里阅读。

有关于这方面更详细的介绍,请移步阅读:

  • 使用 Flexbox 构建经典布局:10 种经典 Web 布局

  • 使用 Grid 构建经典布局:10 种经典布局

在使用 Flexbox 或 Grid 实现均分列布局时,需要考虑最小内容尺寸带来的影响。要是你对这方面感兴趣,请移步阅读《Flexbox 和 Grid 中的最小内容尺寸》。当然,你要是想彻底整明白其中的底层原理,你需要对 flexfr 的计算有一定的了解。如果你不知道它们是如何计算的,个人建议你花一些时间阅读下面这几篇教程:

  • Flexbox 布局中的 flex 属性的基础运用

  • Flexbox 中的计算:通过扩展因子比例来扩展 Flex 项目

  • Flexbox 中的计算:通过收缩因子比例收缩 Flex 项目

  • Flexbox 布局中的 flex-basis:谁能决定 Flex 项目的大小?

  • Grid 布局中的计算

「圣杯布局」

圣杯布局(Holy Grail Layout)是 Web 中典型的布局模式。看上去像下图这样:

对于圣杯布局而言,HTML 结构是有一定的要求,那就是内容为先:

<!-- HTML -->
<header></header>
<main>
    <article></article> <!-- 主内容 -->
    <nav></nav>
    <aside></aside>
</main>
<footer></footer>

在这里主要还是和大家一起探讨,如何使用 Flexbox 和 Grid 布局模块来实现圣杯布局。先来看 Flexbox 实现方案:

body {
    width: 100vw;
    display: flex;
    flex-direction: column;
}

main {
    flex: 1;
    min-height: 0;

    display: flex;
    align-items: stretch;
    width: 100%;
}

footer {
    margin-top: auto;
}

nav {
    width: 220px;
    order: -1;
}

article {
    flex: 1;
}

aside {
    width: 220px;
}

效果如下:

Demo 地址:https://codepen.io/airen/pen/rNxJXYb

通过在navasidearticle上显式设置order的值,可以很好的控制这三个区域的布局顺序。比如说,希望<aside><article>之前排列,只需要在上面的示例基础上做一点点调整:

nav {
    order: 0;
}

aside {
    order: -1;
}

效果如下:

「注意,」 order 「的默认值为」 0 「,值越大越排在后面!」

在上例的基础上,借助 CSS 媒体对象的特性,可以很容易实现响应式的圣杯布局效果:

@media screen and (max-width: 800px) {
    main {
        flex-direction: column;
    }
    nav, aside {
        width: 100%;
    }
}

尝试着拖动浏览器来改变视窗大小,你可以看到如下图的效果:

Demo 地址:https://codepen.io/airen/pen/gOPvVZX

在 Grid 布局模块中,实现圣杯布局要比 Flexbox 布局模块中更容易,而且更灵活。在 CSS Grid 布局模块中,HTML 结构可以更简洁:

<!-- HTML -->
<body>
    <header></header>
    <main></main>
    <nav></nav>
    <aside></aside>
    <footer></footer>
</body>

在 CSS 方面有很多种方案可以实现圣杯布局效果。我们先来看第一种:

body {
    display: grid;
    grid-template: auto 1fr auto / 220px 1fr 220px;
}

header {
    grid-column: 1 / 4;
}

main {
    grid-column: 2 / 3;
    grid-row: 2 / 3;
}

nav {
    grid-column: 1 / 2;
    grid-row: 2 / 3;
}

aside {
    grid-column: 3 / 4;
    grid-row: 2 / 3;
}

footer {
    grid-column: 1 / 4;
}

上面示例采用的是网格线来给每个区域进行定位的:

Demo 地址:https://codepen.io/airen/pen/PoZRYPa

和 Flexbox 布局类似,在媒体查询中可以改变每个网格区域的位置:

@media screen and (max-width: 800px) {
    body {
        grid-template-rows: auto;
        grid-template-columns: auto;
    }

    header,
    main,
    nav,
    aside,
    footer {
        grid-column: 1 / 2;
        min-height: auto;
    }

    main {
        grid-row: 3 / 4;
        margin: 0;
    }

    nav {
        grid-row: 2 / 3;
    }

    aside {
        grid-row: 4 / 5;
    }

    footer {
        grid-row: 5 / 6;
    }
}

Demo 地址:https://codepen.io/airen/pen/vYLRBaa

除了grid-template(即grid-template-columnsgrid-template-rows)之外,在 Grid 布局中还可以使用grid-areagrid-template-areas属性的结合,也能很方便的实现 CSS 圣杯布局。基于上面的示例上,只需要把你的 CSS 调整为:

body {
    display: grid;
    grid-template-areas:
        "header header header"
        "nav main aside"
        "footer footer footer";
}

header {
    grid-area: header;
}

main {
    grid-area: main;
}

nav {
    grid-area: nav;
}

aside {
    grid-area: aside;
}

footer {
    grid-area: footer;
}

@media screen and (max-width: 800px) {
    body {
        grid-template-areas:
            "header"
            "nav"
            "main"
            "aside"
            "footer";
    }
}

Demo 地址:https://codepen.io/airen/pen/LYGdRrG

你可能发现了它们之间的差异性:

后面这个示例中,<nav><main><aside>区域宽度相等。这是因为我们示例中通过grid-template-areas来声明网格,在使用grid-template-areas创建网格时,其实也隐式的创建了网格线,只不过他和grid-template不同的是grid-template可以显式的指定网格轨道大小,而grid-template-areas在该示例中相当于网格轨道大小都是1fr

如果我们希望<main>的区域变得更大,那么可以在grid-template-areas上做个调整:

body {
    display: grid;
    grid-template-areas:
        "header header header header header"
        "nav main main main aside"
        "footer footer footer footer footer";
}

这个时候网格区域的划分像下图这样:

虽然在效果有所调整了,但还是均分状态。更好的解决方案是,将grid-template-areasgrid-template结合起来使用:

body {
    display: grid;
    grid-template-areas:
        "header header header"
        "nav main aside"
        "footer footer footer";
    grid-template-columns: 220px 1fr 220px;
    grid-template-rows: auto 1fr auto;
}

header {
    grid-area: header;
}

main {
    grid-area: main;
}

nav {
    grid-area: nav;
}

aside {
    grid-area: aside;
}

footer {
    grid-area: footer;
}

@media screen and (max-width: 800px) {
    body {
        grid-template-areas:
            "header"
            "nav"
            "main"
            "aside"
            "footer";
        grid-template-columns: 1fr;
        grid-template-rows: auto auto 1fr auto auto;
    }

    main {
        margin-left: 0;
        margin-right: 0;
    }
}

你可以发现,这个时候,网格线的区域的命名像下图这样:

Demo 地址:https://codepen.io/airen/pen/OJMvRev

有关于这方面更详细的介绍,请移步阅读:

  • 使用 Flexbox 构建经典布局:10 种经典 Web 布局

  • 使用 Grid 构建经典布局:10 种经典布局

「12 列网格布局」

**「12 列网格」**布局最早是由 960.gs 提出的网格布局系统:

12 列网格布局在设计系统和 CSS Framework 中经常使用,比如业内经典的 Bootstrap 就采用了 12 列网格布局系统:

在社区中也有很多在线工具,帮助我们快速构建 12 列网格系统,比如 「Free CSS Grid Tools & Resources For Developers」 一文中罗列的工具。

不过这里主要是想和大家一起看看在 Flexbox 和 Grid 布局模块中是如何实现 12 列的网格布局系统。

先来看 Flexbox 布局模块。12 列网格布局的 HTMl 结构一般类似于下面这样:

<!-- HTML -->
<flex__grid>
    <flex__row>
        <flex__item col4></flex__item col4>
        <flex__item col4></flex__item col4>
        <flex__item col4></flex__item col4>
    </flex__row>
</flex__grid>

注意,「12 列网格中,一般同一行的列数值和刚好等于」 12。比如上面的 HTML 结构,行中有三列,每列的宽度刚好四个网格宽度加两个列间距。并且在计算的时候有一套成熟的计算公式:

而且在设计上也会有所差异,比如说距离容器两侧有没有间距等:

这些的差异对于计算公式和样式代码的设计都略有差异。我们用其中一个为例:

:root {
    --gutter: 10px;
    --columns: 12;
    --span: 1;
}

.flex__container {
    display: flex;
    flex-direction: column;
    padding-left: var(--gutter);
    padding-right: var(--gutter);
}

.flex__row {
    display: flex;
    margin-left: calc(var(--gutter) * -1);
    margin-right: calc(var(--gutter) * -1);
}

.flex__row + .flex__row {
    margin-top: 2vh;
}

.flex__item {
    flex: 1 1
        calc((100% / var(--columns) - var(--gutter)) * var(--span));
    margin: 0 var(--gutter);
}

.flex__item1 {
    --span: 1;
}

.flex__item2 {
    --span: 2;
}

.flex__item3 {
    --span: 3;
}

.flex__item4 {
    --span: 4;
}

.flex__item5 {
    --span: 5;
}

.flex__item6 {
    --span: 6;
}

.flex__item7 {
    --span: 7;
}

.flex__item8 {
    --span: 8;
}

.flex__item9 {
    --span: 9;
}

.flex__item10 {
    --span: 10;
}

.flex__item11 {
    --span: 11;
}

.flex__item12 {
    --span: 12;
}

你会看到的效果如下:

Demo 地址:https://codepen.io/airen/pen/YzwaxwX

在该示例中采用了 CSS 自定义属性相关的特性,让整个计算变得更容易一些。

对于使用 CSS Grid 布局模块来实现 12 列网格布局,相对而言,不管是 HTML 结构还是 CSS 代码都会更简易一些。在使用 CSS Grid 布局模块实现 12 列网格布局,将会运用到repeat()minmax()gapfr等特性。具体的来看一个示例吧。

<!-- HTML -->
<grid__container>
    <grid__item></grid__item>
</grid__container>

我们来看 CSS 代码:

  • 使用fr将网格均分为相等的值,即每列宽度都是1fr;配合repeat()函数,即repeat(12, 1fr)创建了 12 列网格

  • 使用gap可以用来控制网格之间的间距

  • 配合minmax()还可以设置网格最小值

具体的代码如下:

:root {
    --columns: 12;
    --gap: 10px;
    --span: 1;
}

.grid__container {
    display: grid;
    grid-template-columns: repeat(var(--columns), 1fr);
    grid-template-rows: 1fr;
    gap: var(--gap);
    padding-left: calc(var(--gap) / 2);
    padding-right: calc(var(--gap) / 2);
}

.grid__item {
    min-block-size: 10vh;
    grid-column: span var(--span);
}

.col1 {
    --span: 1;
}

.col2 {
    --span: 2;
}

.col3 {
    --span: 3;
}

.col4 {
    --span: 4;
}

.col5 {
    --span: 5;
}

.col6 {
    --span: 6;
}

.col7 {
    --span: 7;
}

.col8 {
    --span: 8;
}

.col9 {
    --span: 9;
}

.col10 {
    --span: 10;
}

.col11 {
    --span: 11;
}

.col12 {
    --span: 12;
}

你将看到的效果如下:

Demo 地址:https://codepen.io/airen/pen/yLeKPPb

就该示例而言,grid-template-columns: repeat(12, 1fr)创建网格如下图所示:

除了上述这种粗暴的方式,还可以更灵活一些,将auto-fitminmax()以及grid-auto-flow: dense等来创建:

.grid__container {
    padding: 1em;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(60px, 1fr));
    gap: 1em;
    grid-auto-flow: dense;
}

对于.grid__item可以通过grid-columngrid-row来控制网格项目的位置:

Demo 地址:https://codepen.io/airen/full/QWymabq

加上grid-auto-flow: dense会根据 Grid 容器空间,Grid 项目会自动流到合适的位置:

有关于这方面更详细的介绍,请移步阅读:

  • 使用 Flexbox 构建经典布局:10 种经典 Web 布局

  • 使用 Grid 构建经典布局:10 种经典布局

「两端对齐」

在 Web 布局中时常碰到两端对齐的需求。在 Flexbox 布局中,时常在 Flex 容器中显式设置justify-content的值:

.flex__container {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;

    width: 100%;
}

但在末尾行,如果和前面行的个数不相同(Flex 项目)就会出现下图这样的效果:

像上图这样的效果,并不是我们所需要的,因为我们希望在最后一行的 Flex 项目不足够排列满一行时,希望 Flex 项目一个紧挨一个的排列:

在 Flexbox 要实现上图这样的效果,只需要在 Flex 容器中添加一个伪元素:

.flex__container::after {
    content: "";
    display: flex;
    flex: 0 1 32vw;
}

注意,伪元素的flex-basis建议设置的和卡片的flex-basis(或宽度)等同。这个时候你将看到像下面这样的示例:

Demo 地址:https://codepen.io/airen/full/QWymaam

不过这种方式也不是最佳的方式,当末尾行的个数不只少一个时,就会出现下图这样的效果:

面对这样的场景,我们需要给 Flex 容器添加额外的空标签元素:占位符元素数量 = 每行最大的列数 - 2

但是gap属性出现之后,要实现这样的效果就不难了:

body {
    padding: 1vh;
}

.flex__container {
    display: flex;
    flex-wrap: wrap;
    gap: 2vh;

    width: 100%;
}

.flex__item {
    flex: 0 1 calc((100vw - 8vh) / 4);
}

效果如下:

Demo 地址:https://codepen.io/airen/full/YzwaYBN

在 CSS Grid 布局中,就可以直接使用gap

body {
    padding: 1vh;
}

.grid__container {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 1vh;
}

效果如下:

Demo 地址:https://codepen.io/airen/full/eYJMVbz

「选择最佳的值」

很多时候,针对不同的场景,设计师会为我们提供不同的设计风格,比如元素大小:

随着clam()函数的到来,这一切都变得容易地多。

clam()函数接受三个参数,即 clam(MIN, VAL, MAX) ,其中MIN表示最小值,VAL表示首选值,MAX表示最大值。它们之间:

  • 如果VALMINMAX之间,则使用VAL作为函数的返回值;

  • 如果VAL大于MAX,则使用MAX作为函数的返回值;

  • 如果VAL小于MIN,则使用 MIN 作为函数的返回值

我们来看一个示例:

.element {
    /**
    * MIN = 100px
    * VAL = 50vw ➜ 根据视窗的宽度计算
    * MAX = 500px
    **/ 
    width: clamp(100px, 50vw, 500px);
}

比如浏览器视窗现在所处的位置是1200px的宽度,那么.element渲染的结果如下:

这个时候.element元素的width500px。此时,clamp(100px, 50vw, 500px)相当于clamp(100px, 600px, 500px),对应的VAL值是600px,大于MAX值,那么这个时候clamp()函数返回的值是MAX,即500px,这个时候.elementwidth值就是500px(即MAX的值)。

如果我们把浏览器视窗缩小至760px

这个时候.element元素的width50vw。此时,clamp(100px, 50vw, 500px)相当于clamp(100px, 380px, 500px),对应的VAL值是380px,该值大于MIN值(100px),小于MAX值(500px),那么这个时候clamp()函数返回的值是VAL,即50vw,这个时候.elementwidth值就是50vw(即VAL的值)。

如果继续将浏览器的视窗缩小至170px:

这个时候.element元素的width100px。此时,clamp(100px, 50vw, 500px)相当于clamp(100px, 85px, 500px),对应的VAL值是85px,该值小于MIN值(100px),那么这个时候clamp()函数返回的值是MIN,即100px,这个时候.elementwidth值就是100px(即MIN的值)。

就该示例而言,clamp(100px, 50vw, 500px)还可以这样来理解:

  • 元素.element的宽度不会小于100px(有点类似于元素设置了min-width: 100px

  • 元素.element的宽度不会大于500px(有点类似于元素设置了max-width: 500px

  • 首选值VAL50vw,只有当视窗的宽度大于200px且小于1000px时才会有效,即元素.element的宽度为50vw(有点类似于元素设置了width:50vw

具体效果如下:

Demo 地址:https://codepen.io/airen/pen/pojVpJv

有关于这方面的更详细的介绍,建议你移步阅读:

  • 如何构建响应式 UI

  • 条件 CSS 之 CSS 属性 / 值和 CSS 函数

  • CSS 的比较函数:min(), max() 和 clamp()

「Logo 图标的对齐」

我想你在 Web 开发中可能碰到过类似下图的这样的场景:

正像上图所示,Logo 图像的有大有小(宽度和高度都不一样)。面对这样的业务场景,很多时候都希望设计师能提供相同尺寸的图像。但这样势必会影响 Logo 图像的外观。

其实实现这样的布局效果,主要运用到的就是 CSS 的object-fit属性,而这个属性早在多年前就得到了各大主流浏览器的支持。

这里我们用一个简单的示例,来看看具体实现过程。先来看 HTML 结构:

<!-- HTML -->
<ul class="brands">
    <li class="brands__item">
        <a href="#">
            <img src="img/logo.png" alt="">
        </a>
    </li>
    <li> <!-- ... --> </li>
</ul>

居中对齐前面已经介绍过了,这里主要是看图像大小方面的处理:

.brands {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    grid-gap: 1rem;
}

.brands__item {
    background: #eee;
}

.brands__item a {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
}

.brands__item img {
    width: 130px;
    height: 75px;
    object-fit: contain;
}

这样就能实现上图的效果。你可能发现了,有些 Logo 图像带有背景颜色,如果让效果更好一些,可以把 CSS 混合模式相关的特性运用进来:

.brands__item img {
    width: 130px;
    height: 75px;
    object-fit: contain;
    mix-blend-mode: multiply;
}

这个时候,你看到的效果如下:

object-fit除了取值contain之外,还有其他几个值,具体的可以看下面这个示例:

Demo 地址:https://codepen.io/airen/full/VweXXoo

其实这个方案也适用于产品图片,人物头像等布局。

如果你想了解更多关于 Web 上图片处理相关的技术,建议你花点时间阅读下面这些相关教程:

  • Web 图片:你不应该遗忘的 CSS 技巧

  • 图片的裁剪术

  • 重新思考 Web 中的裁剪:CSS 还是 SVG

  • CSS 的 Clipping 和 Masking

  • CSS 的 object-view-box

  • CSS 图像处理与特效指南

「小结」

文章中主要介绍了 Web 中一些布局的实现思路和具体方案。其实文章提到的效果,比如**「水平垂直居中」「等高布局」「平均分布列」「Sticky Footer」**等,在 CSS 中一直有多种解决方案,只不过随着 CSS Flexbox 布局模块和 CSS Grid 布局模块的到来,实现这些效果变得更为灵活和简洁。

当然,文章中提到的只是一些最为常见一些效果,其实在 Web 布局中,特别是 Flexbox 布局和 Grid 布局中还存在很多有意思的东西,只不过因为篇幅的时间没有一一罗列。如果你感兴趣可以再挖掘一些出来,如果你在这方面有更好的经验或方案,欢迎在下面的评论中分享。最后希望这篇文章对你平时的工作有所帮助。