前端开发者学堂 - fedev.cn

图解CSS:Grid布局案例之构建交叉布局

发布于 大漠

前两个有关于 CSS Grid 案例,主要介绍了如何使用 CSS Grid 来构建 重叠布局Full-Bleed 布局。今天我们来一起再来看另一个由 CSS Grid 构建的网格布局,即 交叉布局。通过这个案例来更好的帮助我们更进一步理解 CSS Grid 的相关属性在实际中(Web 布局)如何使用。

什么是交叉布局

什么是交叉布局?或者说交叉布局是什么样子的。使用一张图来向大家展示什么是交叉布局,毕竟一图胜过千言万言。

我们可以在上面的每个不同的颜色区域中填充 Web 中所需要的媒体元素:

上面这样的 UI 布局常称为 交叉布局。我们可以在这个基础进行扩展,让其变得更复杂一点:

看上去不复杂,很简单,对吧。我在 CodePen 上搜索了一下交叉布局,可以找到相似的布局效果。比如 @Soapp使用 CSS Grid 构建的布局

接下来,我们就以此例作为今天的目标,看看使用 CSS Grid 布局这样的布局会用到网格布局哪些特性。

网格骨架

使用浏览器网格审查器,可以一目了然的知道@Soapp使用 CSS Grid 构建的布局的网格分布:

在 CSS Grid 中,我们可以使用很多方法来定义上图这样的网格,来构建所需的布局。如果忽略布局的中的内容元素(内容可以根据你的喜好填充),就留下了网格形态和一个网格骨架图:

从上图的网格骨架图中,不能发现,我们需要显式创建一个 4 x 3 (三行四列) 的网格,同时每个网格项目都有相应的网格单元格合并(跨越列或跨越行)。

不难发现,这是一个含有五个网格项目的网格布局,相应的 HTML 结构如下:

<!-- HTML -->
<div class="grid__container">
    <div class="grid__item">
        <!-- 跨越3列 -->
    </div>

    <div class="grid__item">
        <!-- 跨越 2行 -->
    </div>

    <div class="grid__item">
        <!-- 跨越 2行 -->
    </div>

    <div class="grid__item">
        <!-- 跨越 2列 -->
    </div>

    <div class="grid__item">
        <!-- 跨越 3列 -->
    </div>
</div>

创建网格容器

首先要做的第一件就是在 .grid__container 容器上显式设置 displaygridinline-grid 创建一个网格容器:

.grid__container {
    display: grid;
}

这个时候你只能看到只有一列的网格:

而我们目标是要创建一个三行四列的网格。因此,我们需要在网格容器上显示使用 grid-template-columnsgrid-template-rows 显式设置网格轨道的尺寸,即列轨道和行轨道大小。比如说,我们把行设置为最小 100px,同时又希望网格行轨道高度通通根据网格项目内容的大小相应地增长。实现这样的行轨道尺寸设置,可以使用 minmax() 函数,即:

.grid__container {
    grid-template-rows: repeat(3, minmax(100px, max-content));
}

按同样的方式,使用grid-template-columns 显式指定网格列轨道的尺寸。比如:

.grid__container {
    grid-template-columns: 200px 1fr 1fr 200px;
}

这样一来,离我们需要的构建的 4 x 3 网格越来越近了。离我们所需的目标网格只差网格轨道之间的间距了。这个在 CSS Grid 布局中很简单,只需要在网格容器中显式设置一个 gap 值即可:

.grid__container {
    gap: 20px;
}

最终运用于网格容器的代码如下:

.grid__container {
    display: grid;
    grid-template-rows: repeat(3, minmax(100px, max-content));
    grid-template-columns: 200px 1fr 1fr 200px;
    gap: 20px;
}

放置网格项目

现在网格容器已经创建好了。接下来要做的是把网格项目放置到所需的位置。在 CSS Grid 布局中,可以显式给 grid-columngrid-row 或它们的简写属性 grid-area 指定网格线名称:

  • grid-column: column-start / column-end
  • grid-row: row-start / row-end
  • grid-area: row-start / column-start / row-end / column-end

在介绍网格线的时候就已经知道了,在显式使用 grid-template-columnsgrid-template-rows 定义网格轨道之后的网格,会自动创建以数值为索引的网格线名称:

  • 列网格线数字索引从左侧到右侧是以 1 递增,同时从右侧到左侧以 -1 递减
  • 行网格线数字索引从顶部到底部是以 1 递增,同时从底部到顶部以 -1 递减

比如,我们可以在相应的网格项目中显式使用 grid-columngrid-row 设置网格线数字索引号来明确放置网格项目:

.header {
    grid-column: 1 / 4;
}

.trophies {
    grid-row: 2 / 4;
}

.info {
    grid-row: 1 / 3;
    grid-column: 4;
}

.photo {
    grid-column: 2 / 4;
}

.bio {
    grid-column: 2 / 5;
}

可以使用 grid-area 来替代 grid-columngrid-row,可以让网格项目按四条网格线来明确放置网格项目:

/* grid-area: row-start / column-start / row-end / column-end */
.header {
    grid-area: 1 / 1 / 2 / 4;
}

.trophies {
    grid-area: 2 / 1 / 4 / 2;
}

.info {
    grid-area: 1 / 4 / 3 / 5;
}

.photo {
    grid-area: 2 / 2 / 3 / 4;
}

.bio {
    grid-area: 3 / 2 / 4 / 5;
}

另外,可以在网格容器使用 grid-template-areas 来定义网格区域,网格区域正好与合并单元格的网格项目位置等同:

.grid__container {
    grid-template-areas:
        "header   header header  info"
        "trophies photo  photo   info"
        "trophies bio    bio     bio";
}

此时,我们可以直接把网格区域的名称运用到 grid-area 属性上:

.header {
    grid-area: header;
}

.trophies {
    grid-area: trophies;
}

.info {
    grid-area: info;
}

.photo {
    grid-area: photo;
}

.bio {
    grid-area: bio;
}

最终得到的效果和使用数字索引号网格线名称一样。而且,采用这样的思路,可以实现很多类似的网格布局:

塞入所需内容

现在网格容器有了,网格项目也按照所需放置到指定位置。我们接下来要做的就是网格项目中塞入所需的内容。再添加一点美化内容的 CSS,你将看到最终的效果如下:

采用同样的方案,就很容易构建像下图这样的布局效果:

注意,这个示例也是一个 Flexbox 和 Grid 相结合的示例。

另外,塞入的内容除了使用 Flexbox 布局,也可以继续使用 Grid 布局,这个时候就是一个典型的网格嵌套网格布局。比如 @Olivia Ng 在 CodePen 的这个示例

开启网格审查器,可以看到网格之间的嵌套关系:

借助 clip-path 构建海报效果

在上面的示例基础上,加上一点其他 CSS 技巧就可以实现一些其他的布局效果。比如说,CSS 的 clip-path,就可以构建一个电影海报图的布局效果:

类似上图这样的布局效果,有一些术语(关键词)可以搜索到,比如“Superhero Layout” 和 “Comics Layout”!

我来看使用 CSS Grid 构建一个这样的示例。构建该布局效果所需的 HTML 结构很简单:

<!-- HTML -->
<div class="grid__container">
    <figure>
        <img src="" alt="" >
    </figure>
</div>

和前面示例一样,使用 grid-template-* 相关属性创建一个交叉网格布局,并且使用 grid-area 明确放置网格项目:

.grid__container {
    display: grid;

    grid-template-columns: 1fr 0.7fr 0.3fr 1fr;
    grid-template-rows: 20% 40% 20% 20%;
    grid-template-areas:
        "one two two three"
        "four five five five"
        "six five five five"
        "six seven eight eight";
}

.one {
    grid-area: one;
}

.two {
    grid-area: two;
}

.three {
    grid-area: three;
}

.four {
    grid-area: four;
}

.five {
    grid-area: five;
}

.six {
    grid-area: six;
}

.seven {
    grid-area: seven;
}

.eight {
    grid-area: eight;
}

.nine {
    grid-area: 3 / 2 / 4 / 4;
}

然后在每个网格项目中使用 clip-path 对网格项目做裁剪:

.one {
    grid-area: one;
    clip-path: polygon(0% 0%, 93.24% 0%, 105.04% 110.16%, 0% 90%);
}

.two {
    grid-area: two;
    clip-path: polygon(0% 0%, 108.28% 0%, 96.45% 110.13%, 10.55% 110.93%);
}

.three {
    grid-area: three;
    clip-path: polygon(15.05% 0%, 100% 0%, 99.35% 91.7%, 3.08% 108.48%);
}

.four {
    grid-area: four;
    clip-path: polygon(
        0% -0.85%,
        106.34% 9.98%,
        121.32% 65.63%,
        99.66% 109.89%,
        1.86% 124.41%
    );
}

.five {
    grid-area: five;
    clip-path: polygon(
        44px 117px,
        25px 26px,
        213px 21px,
        381px 0px,
        379px 144px,
        380px 170px,
        379px 210px,
        368px 269px,
        202px 284px,
        211px 193px,
        159px 184px,
        17px 203px
    );
}

.six {
    grid-area: six;
    clip-path: polygon(2.14% 29.3%, 99.34% 15.42%, 98.14% 100.82%, 1.57% 101.2%);
}

.seven {
    grid-area: seven;
    clip-path: polygon(7.92% 33.47%, 96.31% 23.39%, 95.38% 100%, 5.3% 100.85%);
}

.eight {
    grid-area: eight;
    clip-path: polygon(2.5% 22.35%, 100% 0%, 100% 100%, 1.55% 100%);
}

.nine {
    grid-area: 3 / 2 / 4 / 4;
    clip-path: polygon(
        5.94% 28.66%,
        100.61% -0.67%,
        101.1% 108.57%,
        5.4% 126.28%
    );
}

你可能会好奇,clip-path 属性中每个顶点位置是怎么获取的是吧?其实很简单,我们可以直接在浏览器的开发者工具中,使用 “Shapes” 工具获取坐标点:

最后,在裁剪好的网格项目中塞入相应图片(img),效果就出来了:

有关于这方面更详细的介绍,还可以阅读 @Anton Ball 的《Superhero Layout — Combining CSS Grid and CSS Shapes》教程,该教程详细介绍了如何使用 CSS Grid 和 clip-path 构建海报布局效果:

@Anton Ball 在 Codepen 上还有很多相似的布局效果