现代 CSS

图解CSS: Grid布局(Part19)

特别声明:如果您喜欢小站的内容,可以点击申请会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!或添加QQ:874472854(^_^)

CSS Grid 模块的出现以及浏览器对其支持,为 Web 布局提供了前所未有的可能性。我们可以用更少的元素(更简洁的 HTML 结构)构建更复杂的设计。这比我们一直认为非常强大的 Flexbox 要更强很多。但是,当你想到 CSS Grid 时,你一般会想到是我们平时习以为常的方块布局,对吗?

@Andy Barefoot他的个人网站Codepen 上提供了很多有创意的响应式布局效果,让你会对 Web 布局有一种焕然一新的感觉,感觉与你的经典 Web 设计(规规矩矩方块布局),而且他使用了 CSS Grid 布局来做到这一点。

正如 个人网站采用的 CSS Grid 网格布局,比如 3D 盒子,为不三不同面构建不同的网格系统:

@Andy Barefoot

首页中的每个 3D 盒子对应的是一个 Grid 案例,而且都是很有创意的案例,并且在 Codepen 都有较高的阅读量和点赞数。我们来看几个有意思的案例:

这几个案例除了创意新颖之外,他们还有一个共同的特性,具有响应式网格布局:

案例背景和特征

接下来以上面两个案例为示例,和大家一起聊聊,如何基于 CSS Grid 布局模块和其他 CSS 特性来实现上图的 Web 设计效果。

这两个示例都是响应式布局,随着视窗大小改变,布局也会调整:

除了具有响应式效果之外,它们还有一个共同的特征:不同层的叠加。另外,它们都采用了相似的 CSS 特性,比如 CSS 网格布局、自定义属性 和媒体查询等。

设置网格

先从 HTML 结构开始:

有着非常相似的 HTML 结构,用极简的 DOM 结构来完成很多事情。

<!-- HTML: Case1 -->
<ul>
    <li>
        <img src="fashion1.png" >
        <div>
            <h2> The Ruched Air Blouse</h2>
            <p>$55</p>
        </div>
    </li>
</ul>

<!-- HTML Case2 -->
<ul>
    <li>
        <img src="book.png" />
    </li>
</ul>

让我们来谈谈网格的设置,即ul创建网格容器。另外我们会用到响应式,因此在整个示例中离不开 CSS 的媒体查询,同时为了更为灵活的控制CSS,将还会使用 CSS的自定义属性。

同样遵循响应式设计的理念之一:移动端先行,为此先为移动端声明网格列轨道数量:

/* 为最小的视口设置默认列轨道数量 */

:root {
    --columns: 1;
}

/* 根据 --columns 来制作同等大小的列 */

ul {
    display: grid;
    grid-template-columns: repeat(var(--columns), 1fr);
}

使用 @media 在不同断点改变 --columns 的值即可:

/* Case 1: Product*/
@media (min-width:600px){
    :root {
        --columns: 2;
    }
}
@media (min-width:900px){
    :root {
        --columns: 3;
    }
}
@media (min-width:1200px){
    :root {
        --columns: 4;
    }
}
@media (min-width:1500px){
    :root {
        --columns: 5;
    }
}
@media (min-width:1800px){
    :root {
        --columns: 6;
    }
}


/* Case 2: Book */
@media (min-width:600px){
    :root {
        --columns: 5;
    }
}
@media (min-width:900px){
    :root {
        --columns: 7;
    }
}
@media (min-width:1200px){
    :root {
        --columns: 9;
    }
}
@media (min-width:1500px){
    :root {
        --columns: 11;
    }
}
@media (min-width:1800px){
    :root {
        --columns: 13;
    }
}
@media (min-width:2100px){
    :root {
        --columns: 15;
    }
}

设置好网格,就需要使用 grid-*(比如grid-columngrid-rowsgrid-area)来明确放置网格项目:

/* Case 2: Book */
li {
    grid-column-end: span 2;
}

li:nth-child(2n) {
    grid-column-start: 2;
}

@media (min-width: 600px) {
    li:nth-child(2n) {
        grid-column-start: auto;
    }
    li:nth-child(4n-1) {
        grid-column-start: 2;
    }
}

@media (min-width: 900px) {
    li:nth-child(4n-1) {
        grid-column-start: auto;
    }
    li:nth-child(6n-2) {
        grid-column-start: 2;
    }
}

@media (min-width: 1200px) {
    li:nth-child(6n-2) {
        grid-column-start: auto;
    }
    li:nth-child(8n-3) {
        grid-column-start: 2;
    }
}

@media (min-width: 1500px) {
    li:nth-child(8n-3) {
        grid-column-start: auto;
    }
    li:nth-child(10n-4) {
        grid-column-start: 2;
    }
}

@media (min-width: 1800px) {
    li:nth-child(10n-4) {
        grid-column-start: auto;
    }
    li:nth-child(12n-5) {
        grid-column-start: 2;
    }
}

@media (min-width: 2100px) {
    li:nth-child(12n-5) {
        grid-column-start: auto;
    }
    li:nth-child(14n-6) {
        grid-column-start: 2;
    }
}

注意,Case1中的网格项目是自动放置的。

将形状拼接在一起

示例中的每张卡片都有四个层级:

除了 HTML 元素之外还借助了 CSS 伪元素 ::before::after。他们的层级关系是:

就拿示例2中的书本层级,合在一起的过程和效果:

实现单个图形效果

第一个效果相对而言,要简单很多,花纹前景和背景采用的都是 SVG 图:

其结构建也很简单:

<!-- HTML -->
<div class="card">
    <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/881020/fashion08.png" alt="" />
    <div>
        <h2>The Linen Square-Neck Jumpsuit</h2>
        <p>$88</p>
    </div>
</div>

添加一些 CSS 可以看到单个卡片效果如下:

第二个示例稍为复杂一些:

前面已经介绍了它的实现原理和层级的关系。有了这个基础之后,我们就可以快速使用CSS来完成。它的HTML结构很简单,就是一个元素标签(比如div)包裹了一个<img>标签。

<!-- HTML -->
<div class="box">
    <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/881020/book34.jpg" alt="">
</div>

从效果上可以得知,容器是一个1:1的尺寸,在CSS中有多种方式可以实现该效果,大家可能熟悉的就是通过padding-bottom: 100%来实现1:1的容器(即根据纵宽比来完成):

.box {
    background-color: #eebc1f;
    width: 20vw;
    padding-bottom: 20%;
    transform: rotate(45deg)
}

注意,padding取值为%时根据父元素的width来计算。详细可以参阅《CSS中百分比单位计算方式》一文。

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

但随着aspect-ratio属性的出现,实现1:1的效果更为容易:

.box {
    aspect-ratio: 1 / 1;
}

在这个基础上,通过::before::after两个伪元素来构建另两个层:

.box::before,
.box::after {
    content: "";
    display: block;
    width: 100%;
    height: 100%;
    position: absolute;
    background-color: #068d7e;
}

在这个示例中有一点不一样,采用了最新的渐变属性conic-gradient()绘制所需要的形状效果

.box::before {
    background: conic-gradient(#eebc1f 25%, #068d7e 0 50%, #eebc1f 0) 100% 100% /
    180% 180%;
}

.box::after {
    background: conic-gradient(#eebc1f 75%, #068d7e 0) 0 0 / 180% 180%;
}

离我们的目标越来越近了,为了让<img>的上下两层不会遮盖整个图像,这里需要再用到clip-path属性,裁剪出所需要的效果:

.box::before {
    clip-path: polygon(0 0, 100% 0, 100% 20%, 20% 20%, 20% 100%, 0 100%);
}

.box::after {
    clip-path: polygon(80% 20%, 100% 0, 100% 100%, 0% 100%, 20% 80%, 80% 80%);
}

clip-path中每个点的坐标对应如下:

这个时候,将它位合成在一起就得到我们想要的效果:

实现单个效果之后,要实现整个列表效果就要轻易地多了。要是您对上面绘制图形用到的相关 CSS 技术感兴趣,还可以阅读:

网格布局

在上面我们看到的只是一个列表项的效果,如果我们将N个这样的效果放到一起,就会离我们想要的效果越来越近。虽然列表项目的数量多起来了,但它们的结果是相同的:

<!-- HTML -->
<ul>
    <li><img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/881020/book10.jpg" alt=""></li>
    <!-- N个li -->
</ul>

在这里采用的是grid布局:

ul{
    display:grid;
    grid-template-columns: repeat(var(--columns),1fr);
}

注意,在上面的示例代码中有--columns,这个是CSS的自定义属性,在:root选择器中显式声明了:

:root {
    --columns: 3;
}

li:nth-child(2n){
    grid-column-start:2;
}

在构建响应式布局时,依旧是以移动端先行,正如上面的代码所示。在类似手机端的设备,你看到的效果类似下图:

有关于示例更详细的代码就不在这里全部展开,感兴趣的同学可以查看示例源码!

小结

这里仅展示了两个与网格布局相关示例,除了这两个示例,前面还提供了其他几个示例。虽然效果上有所不同,但他们所采用的技术都是相似的,即 都以新颖的方式使用了 CSS Grid。通过借开起始列,或者倾斜整个网格,你可以得到一些非常棒的 Web 设计效果(布局效果)。我很高兴地看到,随着 CSS Grid 在越来越多的生产网站上的应用。

返回顶部