图解CSS: Grid布局(Part19)
CSS Grid 模块的出现以及浏览器对其支持,为 Web 布局提供了前所未有的可能性。我们可以用更少的元素(更简洁的 HTML 结构)构建更复杂的设计。这比我们一直认为非常强大的 Flexbox 要更强很多。但是,当你想到 CSS Grid 时,你一般会想到是我们平时习以为常的方块布局,对吗?
@Andy Barefoot 在他的个人网站 和 Codepen 上提供了很多有创意的响应式布局效果,让你会对 Web 布局有一种焕然一新的感觉,感觉与你的经典 Web 设计(规规矩矩方块布局),而且他使用了 CSS Grid 布局来做到这一点。
正如 个人网站采用的 CSS Grid 网格布局,比如 3D 盒子,为不三不同面构建不同的网格系统:
首页中的每个 3D 盒子对应的是一个 Grid 案例,而且都是很有创意的案例,并且在 Codepen 都有较高的阅读量和点赞数。我们来看几个有意思的案例:
- ①:Technical Product Grid
- ②:Responsive Product Grid with layered background
- ③:Grid of Thrones (Complete)
- ④:CSS Grid Responsive Perspective layout
- ⑤:Responsive Stacked Cubes - CSS Grid
- ⑥:Tessellations eCommerce
- ⑦:Isometric eCommerce CSS Grid
- ⑧:Skewed Responsive eCommerce CSS Grid
- ⑨:Responsive CSS Grid - Books
这几个案例除了创意新颖之外,他们还有一个共同的特性,具有响应式网格布局:
案例背景和特征
接下来以上面两个案例为示例,和大家一起聊聊,如何基于 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-column
、grid-rows
或 grid-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 在越来越多的生产网站上的应用。