CSS——网格布局(display: grid)之下篇
前面我们介绍了网格布局的基础的创建以及一些比较基础的属性,下面我们将介绍网格布局的剩余部分,还将结合实例来进行细致的讲解(图文并茂,生动形象有内涵)。
显式网格和隐式网格
显式网格:是指在CSS中明确定义的网格轨道。包括在 grid-template-rows
和 grid-template-columns
属性中指定的行和列。显示网格的大小和位置是固定的,由开发者在CSS中明确指定。
隐式网格:指在显示网格之外自动创建的网格轨道。当网格项没有足够的显示网格轨道来容纳时,浏览器会自动创建额外的轨道来放置这些网格项。隐式网格的大小和位置是自动计算的,基于网格项的需求和网格容器的尺寸。
如果网格元素放在声明的网格轨道之外,就会创建隐式轨道,直到包含该元素(如上图所示),同时在指定网格线的时候,隐式网格轨道不会改变负数的含义。负的网格线编号仍然是从显式网格的右下开始的。
隐式网格轨道默认的大小为auto
,也就是它们会扩展到能容纳自身的宽度(包含内容、内边距、边框和外边距)。下面介绍三个与之相关的属性。
grid-auto-flow 属性
首先来看取值吧:
grid-auto-flow: row;
grid-auto-flow: column;
grid-auto-flow: dense;
grid-auto-flow: row dense;
grid-auto-flow: column dense;
其中:
- row
多的格子一行一行陈列。默认值。 - column
多的格子一列一列排列。 - dense
多的格子空白填充。
row 与 column 之分
如果我们创建一个网格布局,可以看到,网格元素之间是默认占满一行,然后一个挨着一个地向下排列的。
<style>
.container {
display: grid;
}
.item {
background-color: #ccc;
}
</style>
<body>
<main class="container">
<div class="item">1号盒子</div>
<div class="item">2号盒子</div>
<div class="item">3号盒子</div>
<div class="item">4号盒子</div>
<div class="item">5号盒子</div>
</main>
</body>
如果此时我们为网格容器添加grid-template-columns: 1fr 1fr
,使之分为两列,可以看到:这些盒子在填满本行之后,才去换行显示。那你可能会说了:“这不是理所当然的吗?”,嘿嘿,这可没有那么简单,这是因为grid-auto-flow
属性默认设置为row
,即默认先填充横向,再填充纵向。
如果我们修改grid-auto-flow: column
,
<style>
.container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-auto-flow: column;
}
.item{
background-color: #ccc;
}
</style>
<body>
<main class="container">
<div class="item">1号盒子</div>
<div class="item">2号盒子</div>
<div class="item">3号盒子</div>
<div class="item">4号盒子</div>
<div class="item">5号盒子</div>
</main>
</body>
那么会产生如下的效果:
原因是:我们设置了grid-auto-flow: column
,故就先纵向填满,然而我们没有明确的划分出行数,所以就默认仅有初始元素所在的第一行,这就导致所有的子元素会横向排列成一行,后面的第3、4、5列没有划分大小,所以他们就fit-content
,然后剩下的第1、2列平分剩下的空间( fr
单位的优先级是最低的,优先级顺序以及fit-content
的解释可以看CSS——网格布局(display: grid)之上篇)。
dense 的作用
dense
翻译为稠密的,我们打个比方,如果有一片四四方方的农田,每家每户都各自分有一块,一开始大家都在种。如下图:
直到有一天,中间的一块田的主人王老二进城打工了,这片田就荒废了,远远望去,中间秃了一块。如下图:
村里人看到王老二进城打工赚大钱了,也就纷纷效仿,渐渐的,原本整整齐齐的一大块地,变得稀稀拉拉的。如下图:
这时候村长说了,“现在的地太分散了,为了方便管理,我们重新规划一下地块”。经过又一年的种植,如下图:
大家发现,村长让原本分散的地块变得稠密了,于是就给村长起了个外号“dense”。
我们再用代码演示:我们设置第一个元素在第二列的位置
<style>
.container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
}
.item{
background-color: #ccc;
}
.item:nth-child(1) {
grid-column: 2 / 3;
}
</style>
<body>
<main class="container">
<div class="item">1号盒子</div>
<div class="item">2号盒子</div>
<div class="item">3号盒子</div>
<div class="item">4号盒子</div>
<div class="item">5号盒子</div>
</main>
</body>
可以看到,左上角的位置空出来了(变得稀疏了)
这时,我们添加grid-auto-flow: dense
(row是默认的),会看到:2号盒子填补了空缺的位置
如果我们设置了 grid-auto-flow: column dense
,那么就会是以下的效果:
嗯,dense就是这样的,稍后实例中会用到。
P.S. 貌似有一个bug,如果在明确指定了某个子元素不在其默认位置时,浏览器渲染时会默认产生dense
的效果。何为明确与不明确呢?比如刚才grid-column: 2 / 3
仅指定了子元素的列的位置,没指定行的位置,那么这就算不明确指定;如果grid-column: 2 / 3
& grid-row: 1 /2
或者 gird-area: A
,这个元素的位置是确定的,那么这就算明确指定,在此时,浏览器渲染时会默认产生 dense
的效果。
grid-auto-columns & grid-auto-rows 属性
前面我们看到,在仅规定两列的情况下,隐式网格轨道的大小是 fit-content
的,grid-auto-columns
& grid-auto-rows
两个属性就是规定了隐式轨道的大小。
实战强化
注意:下面的部分会用到Flexbox(弹性盒子)布局
部分的知识,该部分内容可以参考本人的另一篇文章CSS——弹性盒子布局(display: flex)
目标效果
下面我们要实现一个如下图的效果
我们可以看到图片有大有小,但是它们却能够网格对齐,仔细看还会发现,它们的顺序不是按照从左到右排序的,这个可以先联系一下 grid-auto-flow: dense
。
步骤
创建网格布局
<style>
body {
background-color: rgb(254, 226, 181);
font-family: Helvetica, Arial, sans-serif;
}
.portfolio {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-gap: 1em;
}
.portfolio>figure {
margin: 0;
}
.portfolio img {
max-width: 100%;
}
.portfolio figcaption {
padding: 0.3em 0.8em;
background-color: rgba(0, 0, 0, 0.5);
color: #fff;
text-align: right;
}
.portfolio .featured {
grid-row: span 2;
grid-column: span 2;
}
</style>
<body>
<div class="portfolio">
<figure class="featured">
<img src="./images/pic1.jpg" alt="">
<figcaption>1</figcaption>
</figure>
<figure>
<img src="./images/pic2.jpg" alt="" />
<figcaption>2</figcaption>
</figure>
<figure>
<img src="./images/pic3.jpg" alt="" />
<figcaption>3</figcaption>
</figure>
<figure class="featured">
<img src="./images/pic4.jpg" alt="" />
<figcaption>4</figcaption>
</figure>
<figure>
<img src="./images/pic5.jpg" alt="" />
<figcaption>5</figcaption>
</figure>
<figure class="featured">
<img src="./images/pic6.jpg" alt="" />
<figcaption>6</figcaption>
</figure>
<figure>
<img src="./images/pic7.jpg" alt="" />
<figcaption>7</figcaption>
</figure>
<figure class="featured">
<img src="./images/pic8.jpg" alt="" />
<figcaption>8</figcaption>
</figure>
</div>
</body>
我们创建以上的网格布局是这样的:
其中,我们通过grid-template-columns: repeat(auto-fit, minmax(200px, 1fr))
创建了自适应布局,会根据视口的大小自动分配列数,但是每列的最小宽度不会低于200px。
这时候我们发现,容器内部比较稀疏,所以我们想到了grid-auto-flow: dense
,为网格容器添加了以上属性之后的效果如下:
我们会发现2、3号是对齐的,但是5、7号以及6、8号是没有对齐的,这是因为在默认情况下,网格元素会扩展到网格单元的大小,但是其子元素不会自动扩展
,在这里,网格元素是 <figure>
元素,它会自动扩展,但是它的子元素<img>
与 <figcaption>
不会扩展,网格肯定是对齐的,但是由于以上原因,导致网格单元内部出现了空余部分,如下图:
使网格单元内部所有元素拉伸
这时候我们想到了前面的 Flexbox(弹性盒子)布局
,它能够实现其直接子元素自动扩展(与网格布局类似)。所以我们添加以下代码:
.portfolio>figure {
margin: 0;
display: flex;
flex-direction: column;
}
我们首先将<figure>
设置成弹性盒子,让其主轴方向为竖直,那么其子元素就可以实现在竖直方向上的扩展。效果如下:
我们发现,这里除了<figcaption>
与 <img>
元素之间的间距没了,其余的还是没有变化,嘿嘿,这是为什么呢?
当然是我们忘记给子元素设置flex
属性了,可以看到,flex-grow
默认值是 0
即默认不会放大。
所以我们为子元素设置该属性(建议使用简写,原因见本人的 flex文章,链接在本节开始部分):flex: 1
,我们不希望<figcaption>
也随之放大,所以我们只为<img>
添加该属性。
figure>img{
flex: 1;
}
效果如下:
以上看起来以上效果很好了(嗯,我也觉得),前面消失的<img>元素与<figcaption>之间间距我就不追加了,不过有时候要考虑<img>
图片的缩放问题。
一个被拉伸的<img>元素的小细节
在前面我们将<img>元素拉伸以填充满父元素,但是这样这会改变图片的宽高比,导致图片变形。好在 CSS 为控制这一行为提供了一个特殊属性 object-fit
。默认情况下,一个<img>
的 object-fit
属性值为 fill
,也就是说整个图片会缩放,以填满<img>元素。我们也可以设置其他值改变默认行为。
比如,object-fit
属性的值还可以是 cover
和 contain
。这些值告诉浏览器,在渲染盒子里改变图片的大小,但是不要让图片变形。
- cover:扩展图片,让它填满盒子(导致图片一部分被裁剪)。
- contain:缩放图片,让它完整地填充盒子(导致盒子里出现空白)。
这里,我为了保持画面的整体性,默认采用了 object-fit: fill
。可以根据需求和具体情况,选择适合的值。
这里有两个概念要区分清楚:盒子(由<img>元素的宽和高决定)和渲染的图片。默认情况下,这二者大小相等。object-fit 属性让我们能在盒子内部控制渲染图片的大小,同时保持盒子的大小不变。
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网格布局的实战</title>
<style>
body {
background-color: rgb(254, 226, 181);
font-family: Helvetica, Arial, sans-serif;
}
.portfolio {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-auto-flow: row dense;
grid-gap: 1em;
}
.portfolio>figure {
margin: 0;
display: flex;
flex-direction: column;
}
figure>img {
flex: 1;
object-fit: fill;
}
.portfolio img {
max-width: 100%;
}
.portfolio figcaption {
padding: 0.3em 0.8em;
background-color: rgba(0, 0, 0, 0.5);
color: #fff;
text-align: right;
}
.portfolio .featured {
grid-row: span 2;
grid-column: span 2;
}
</style>
</head>
<body>
<div class="portfolio">
<figure class="featured">
<img src="./images/pic1.jpg" alt="">
<figcaption>1</figcaption>
</figure>
<figure>
<img src="./images/pic2.jpg" alt="" />
<figcaption>2</figcaption>
</figure>
<figure>
<img src="./images/pic3.jpg" alt="" />
<figcaption>3</figcaption>
</figure>
<figure class="featured">
<img src="./images/pic4.jpg" alt="" />
<figcaption>4</figcaption>
</figure>
<figure>
<img src="./images/pic5.jpg" alt="" />
<figcaption>5</figcaption>
</figure>
<figure class="featured">
<img src="./images/pic6.jpg" alt="" />
<figcaption>6</figcaption>
</figure>
<figure>
<img src="./images/pic7.jpg" alt="" />
<figcaption>7</figcaption>
</figure>
<figure class="featured">
<img src="./images/pic8.jpg" alt="" />
<figcaption>8</figcaption>
</figure>
</div>
</body>
</html>
结尾
创作不易,感谢喜欢和支持,如有错误,恳请指出,希望与大家共同进步。