CSS 系列之:grid 布局
基本概念
<template>
<div class="parent">
<div class="box">p1-1</div>
<div class="box">p1-2</div>
<div class="box">p1-3</div>
</div>
<div class="parent">
<div class="box">p2-1</div>
<div class="box">p2-2</div>
<div class="box">p2-3</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
display: grid; // 块级容器,宽度撑满整行
/* display: inline-grid; */ // 行内容器,宽度随内容自适应
}
.box {
border: 1px solid #000;
}
</style>
最外层的 <div class="parent">
称为容器,内层的三个 <div class="children">
称为项目(或网格项)。
使用网格布局后,项目的 float、display: inline-block、display: table-cell、vertical-align、column-*
等设置都将失效。
指定行列
grid-template-columns 指定划分列数
grid-template-rows 指定划分行数
固定宽高 px
<template>
<div class="parent">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box box5">5</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
display: grid;
/* 3 列 */
grid-template-columns: 100px 100px 100px;
/* 2 行 */
grid-template-rows: 50px 40px;
}
.box {
border: 1px solid #000;
margin: 10px;
}
</style>
可以看到 grid-template-columns 和 grid-template-rows 设置的值是包含 margin 在内的。
使用了 grid-template-columns 和 grid-template-rows 后还可以单独给项目设置宽高吗?答案是可以的。
.box5 {
height: 80px;
}
单独设置宽高是不包含 margin 的,且容器的宽高不会受它影响。
百分比 %
-
如果
.parent
没有显式设置 width 和 height ,则 100% 将等于 .parent 自然展开后的宽高。 -
如果
.parent
有显式的 width 和 height 值,那么 100% 就等于该指定的宽高值。
<template>
<div class="parent">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
<div class="box">6</div>
<div class="box">
<div style="height: 30px;">7</div>
</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
display: grid;
/* 3 列 */
grid-template-columns: 40% 30% 20%;
/* 2 行 */
grid-template-rows: 100% 50%;
}
.box {
border: 1px solid #000;
margin: 10px;
}
</style>
第 7 个 项目的高度没有被 grid-template-rows
设置到,则第 7 个 div 的高度是他实际内容的高度。
重复设置 repeat
使用 repeat 统一设置值,第一个参数为重复数量,第二个参数是重复值
<template>
<div class="parent">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
<div class="box">6</div>
<div class="box">7</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
display: grid;
/* 3 列 */
grid-template-columns: repeat(3, 40%);
/* 2 行 */
grid-template-rows: repeat(2, 50%);
}
.box {
border: 1px solid #000;
margin: 10px;
}
</style>
项目总宽度超过 100% 时会产生滚动条。
repeat 还可以设置多个值:
<template>
<div class="parent">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
<div class="box">6</div>
<div class="box">7</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
display: grid;
grid-template-columns: repeat(2, 200px 100px);
grid-template-rows: repeat(2, 50px);
}
.box {
border: 1px solid #000;
margin: 10px;
}
</style>
自动填充 auto
用于填充满所有剩余空间
<template>
<div class="parent">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
<div class="box">6</div>
<div class="box">7</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
display: grid;
grid-template-columns: 100px 20% auto;
}
.box {
border: 1px solid #000;
margin: 10px;
}
</style>
比例划分 fr
fr 是 fraction 的缩写,意为"片段"。
<template>
<div class="parent">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
<div class="box">6</div>
<div class="box">7</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
display: grid;
grid-template-columns: 100px 1fr 2fr;
grid-template-rows: 3fr 1fr 2fr;
}
.box {
border: 1px solid #000;
margin: 10px;
}
</style>
假设容器总宽度是 800px,grid-template-columns: 100px 1fr 2fr
则表示第一列占据 100px 宽度,第二列占据剩余 700px 的三分之一的宽度,第三列占据剩余 700px 的三分之二的宽度。
所以 grid-template-columns: 100px 20% 1fr
中最后一列的 1fr 其实也是铺满剩余空间的意思。
repeat 中也能用 fr。
最大最小值 minmax
minmax() 函数产生一个长度范围,表示长度就在这个范围之中。它接受两个参数,分别为最小值和最大值。
<template>
<div class="parent">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
<div class="box">6</div>
<div class="box">7</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
display: grid;
grid-template-columns: 100px 100px minmax(100px, 1fr);
grid-template-rows: 3fr 1fr 2fr;
}
.box {
border: 1px solid #000;
margin: 10px;
}
</style>
最后一列表示宽度最小 100px,最大铺满剩余空间。
自动调整 auto-fit 和 auto-fill
<template>
<div class="parent auto-fit">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
</div>
<div class="parent auto-fill">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
display: grid;
width: 600px;
margin-bottom: 20px;
}
.auto-fit {
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
}
.auto-fill {
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
}
.box {
border: 1px solid #000;
margin: 10px;
}
</style>
容器宽度为 600px,我们希望在其中放置一些最小宽度为 100px 的项目,每个项目的最大宽度为 1fr(即剩余空间的等分部分)。
理论上可以创建 6 个 100px 宽的列,但只有 3 个项目,所以会有 3 个空的列。
auto-fit 会合并多余的空列,使现有列扩展以填满所有可用空间。
auto-fill 仍然会创建 6 个列,但其中 3 个列将是空的。
auto-fill 和 auto-fit 关键需要与 repeat() 函数和 minmax() 函数一起使用,才能发挥它们的作用。
在上例中我们看到,使用 auto-fill 时,项目的宽度始终时 minmax(100px, 1fr)
中的最小值 100px,那么 1fr 的作用是什么呢?
直接说结论:
-
当容器的宽度小于等于项目的宽度时,1fr 不会起作用
-
当容器的宽度是项目的宽度的整数倍时,1fr 不会起作用
-
当容器的宽度是项目的宽度的大于 1 的小数倍时,1fr 才会起作用
例如把上面的例子中改成:
.parent {
width: 460px
}
每个项目宽度是 115px,被扩充了。
总结:
auto-fit 和 auto-fill 都会尝试创建尽可能多的项目,例如 460px 的容器最多可以创建 4 个最小宽度为 100px 的项目,但是 html 中只有 3 个项目,对多出的这 1 个空白项目和剩余的额外 60px 空间的处理方式就是 auto-fit 和 auto-fill 的区别
-
auto-fit 会吞并多出的空白项目,使现有项目扩展以填满所有剩余空间
-
auto-fill 会保留多出的空白项目,空白项目会占据空间,如果有剩余的额外,则所有项目会扩展以填满剩余额外空间
指定区域
grid-template-areas
可以将网格布局中的某个单元格或多个单元格定义为一个区域。
网格区域一定要形成规整的矩形区域,无论是 L 形,还是凹的或凸的形状都会认为是无效的属性值。
按固定数值划分
<template>
<div class="parent">
<div class="box top">top</div>
<div class="box left">left</div>
<div class="box right">right</div>
<div class="box bottom">bottom</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
height: 100%;
display: grid;
grid-template-columns: 50px auto;
grid-template-rows: 70px auto 70px;
grid-template-areas:
"top top"
"left right"
"bottom bottom";
}
.box {
border: 1px solid #000;
}
.top {
grid-area: top;
}
.left {
grid-area: left;
}
.right {
grid-area: right;
}
.bottom {
grid-area: bottom;
}
</style>
按比例划分
<template>
<div class="parent">
<div class="box top">top</div>
<div class="box left">left</div>
<div class="box right">right</div>
<div class="box bottom">bottom</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
height: 100%;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr 1fr;
grid-template-areas:
"top top top"
"left right right"
"left right right"
"bottom bottom bottom";
}
.box {
border: 1px solid #000;
}
.top {
grid-area: top;
}
.left {
grid-area: left;
}
.right {
grid-area: right;
}
.bottom {
grid-area: bottom;
}
</style>
grid-tempalte
是 grid-template-rows、grid-template-columns、grid-template-areas 的三个属性的简写。
按固定数值划分
上面的按固定数值划分可以改造为:
.parent {
background-color: skyblue;
height: 100%;
display: grid;
grid-template:
"top top" 70px
"left right" auto
"bottom bottom" 70px
/ 50px auto;
}
第一行 "top top" 70px
定义了一个名为 top 的区域占据前两列,并且该区域所在行的高度为 70px。
第二行 "left right" auto
定义了一个名为 left 的区域占据第一列和一个名为 right 的区域占据第二列,并且该区域所在行的高度为 auto。
第三行 "bottom bottom" 70px
定义了一个名为 bottom 的区域占据前两列,并且该区域所在行的高度为 70px。
最后的 / 50px auto
定义了列的大小:第一列的宽度为 50px,第二列的宽度为 auto。
按比例划分
上面的按比例划分可以改造为:
.parent {
background-color: skyblue;
height: 100%;
display: grid;
grid-template:
"top top top" 1fr
"left right right" 1fr
"left right right" 1fr
"bottom bottom bottom" 1fr
/ 1fr 1fr 1fr;
}
一行可以有多个不同的高度吗?例如把第一行改成 "top top top" 1fr 50px
。答案是不行,会导致错乱。
点代表占位符
.parent {
background-color: skyblue;
height: 100%;
display: grid;
grid-template:
"top top ." 1fr
"left . ." 1fr
"right . ." 1fr
"bottom bottom bottom" 1fr
/ 1fr 1fr 1fr;
}
注意:
一个元素不能同时占据多个不连续的网格区域。
也就是说 "top top ."
和 ". top . "
、". . top"
等等都是允许的,但是 "top . top"
是不允许的。
其实 top
对应的是一个 html 元素,它要么占据连续的区域,要么占据一个单独的单元格,它不能分裂成不连续的部分。
定义间距
行间距 row-gap
列间距 column-gap
<template>
<div class="parent">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
<div class="box">6</div>
<div class="box">7</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
column-gap: 30px;
row-gap: 20px;
}
.box {
border: 1px solid #000;
}
</style>
组合定义 gap
组合写法 gap: 20px 30px;
先行(row)后列(column)
元素定位
grid-row/column-start/end
样式属性 | 说明 |
---|---|
grid-row-start | 行开始网格线 |
grid-row-end | 行结束网格线 |
grid-column-start | 列开始网格线 |
grid-column-end | 列结束网格线 |
属性值 | 说明 |
---|---|
Line | 网格络 |
span 数值 | 网格包含的网格数量 |
span 区域名称 | 网格包含到指定的区域名称 |
auto | 自动设置,默认为一个网格宽度和高度 |
用于指定一个网格项目的开始行(列)和结束行(列)的位置。
例如:
.grid-item1 {
grid-row-start: 1; /* 从第 1 行开始 */
grid-row-end: 3; /* 到第 3 行结束(不包括第 3 行,即跨越两行) */
}
.grid-item2 {
grid-row-start: 1;
grid-row-end: span 2; /* 从第 1 行开始,跨越 2 行 */
}
行线 1 对应第一行的上边缘
行线 2 对应第二行的上边缘
行线 3 对应第三行的上边缘
…
grid-row-start 通常要比 grid-row-end 小,否则会导致一些意想不到的结果。
<template>
<div class="parent">
<div class="box box1">1</div>
<div class="box box2">2</div>
<div class="box box3">3</div>
<div class="box box4">4</div>
<div class="box box5">5</div>
<div class="box box6">6</div>
<div class="box box7">7</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
gap: 20px 30px;
}
.box {
border: 1px solid #000;
}
.box1 {
grid-row-start: 1;
}
</style>
.box1 {
grid-row-start: 2;
}
.box1 {
grid-row-start: 3;
}
.box1 {
grid-row-start: 4;
}
.box1 {
grid-row-start: 1;
grid-row-end: 3; /* 当该值是 1 和 2 时表现形式和默认图相同,直接从 3 开始 */
}
.box1 {
grid-row-start: 1;
grid-row-end: 4;
}
.box1 {
grid-row-start: 1;
grid-row-end: 5;
}
.box1 {
grid-row-start: 1;
grid-row-end: 6;
}
简写模式 grid-row、grid-column
grid-row 和 grid-column
语法:
grid-row: grid-row-start / grid-row-end;
grid-column: grid-row-column / grid-row-column;
例如:
.box1 {
grid-row-start: 1;
grid-row-end: 6;
}
就可以简写成
.box1 {
grid-row: 1/6;
}
超级简写模式 grid-area
grid-area: grid-row-start / grid-column-start / grid-row-end / grid-column-end;
网格流动 grid-auto-flow
在容器中设置 grid-auto-flow 属性可以改变单元格排列方式。
选项 | 说明 |
---|---|
row | 按行排列,默认值 |
column | 按列排序 |
row dense | 网格将尝试在行方向上尽可能紧密地放置项目,尽量不出现空格 |
column dense | 网格将尝试在列方向上紧密放置项目,尽量不出现空格 |
<template>
<div class="parent">
<div class="box box1">1</div>
<div class="box box2">2</div>
<div class="box box3">3</div>
<div class="box box4">4</div>
<div class="box box5">5</div>
<div class="box box6">6</div>
<div class="box box7">7</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
gap: 20px 30px;
grid-auto-flow: row;
}
.box {
border: 1px solid #000;
}
</style>
grid-auto-flow: column
设置单元格对齐方式
justify-items
指定单元格内容的水平对齐方式
属性 | 描述 |
---|---|
stretch | 默认值,占满单元格的整个宽度 |
start | 对齐单元格的起始边缘 |
end | 对齐单元格的结束边缘 |
center | 单元格内部居中 |
<template>
<div class="parent">
<div class="box box1">1</div>
<div class="box box2">2</div>
<div class="box box3">3</div>
<div class="box box4">4</div>
<div class="box box5">5</div>
<div class="box box6">6</div>
<div class="box box7">7</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: repeat(3, 50px);
gap: 20px;
}
.box {
border: 1px solid #000;
}
</style>
.parent {
justify-items: start
}
.parent {
justify-items: center
}
.parent {
justify-items: end
}
align-items
指定单元格内容的垂直对齐方式
normal | 默认值,会根据使用场景的不同表现为 stretch 或者 start |
stretch | 拉伸,占满单元格的整个宽度 |
start | 对齐单元格的起始边缘 |
end | 对齐单元格的结束边缘 |
center | 单元格内部居中 |
baseline | 基线对齐 |
.parent {
align-items: normal; // 或 stretch
}
.parent {
align-items: start;
}
.parent {
align-items: center;
}
.parent {
align-items: end;
}
place-items
是 align-items 属性和 justify-items 属性的合并简写形式。
.parent {
// 先垂直方向,再水平方向
place-items: start end;
}
IE 浏览器和 Edge 浏览器都不支持 place-items 属性。如果不考虑浏览器的兼容性,在 CSS 中实现垂直居中对齐效果的最佳方法就是使用 grid 布局的 place-items 属性:
.parent {
// 如果省略第二个值,则浏览器认为与第一个值相等
place-items: center;
}
justify-self
跟 justify-items 属性的用法完全一致,但只作用于单个项目。
.box1 {
justify-self: center
}
align-self
跟 align-items 属性的用法完全一致,也是只作用于单个项目。
.box1 {
align-self: center
}
place-self
是 align-self 属性和 justify-self 属性的合并简写形式,跟 place-items 属性的用法完全一致。
.box1 {
place-self: center
}
设置容器内的对齐方式
justify-content
项目在水平方向对齐方式
align-content
项目在垂直方向对齐方式
与 flex 布局中的类似。
注意:要想 justify-content 属性和 align-content 属性起作用,就需要让项目的总尺寸小于 grid 容器的尺寸。
<template>
<div class="parent">
<div class="box box1">1</div>
<div class="box box2">2</div>
<div class="box box3">3</div>
<div class="box box4">4</div>
<div class="box box5">5</div>
<div class="box box6">6</div>
<div class="box box7">7</div>
</div>
</template>
<style scoped>
.parent {
background-color: skyblue;
display: grid;
grid-template-columns: repeat(3, 50px);
grid-template-rows: repeat(3, 50px);
gap: 20px;
width: 100%;
height: 300px;
}
.box {
border: 1px solid #000;
}
</style>
.parent {
justify-content: space-between;
}
place-content
是 align-content 属性和 justify-content 属性的合并简写形式。如果省略第二个值,浏览器就会假定第二个值等于第一个值。
.parent {
// 先垂直方向,再水平方向
place-content: center space-between;
}
xxx-item 和 xxx-content 的区别
xxx-item 用于控制容器内每个项目的对齐方式。它影响的是每个单独的项目在其所在单元格内的对齐方式。
xxx-content 用于控制容器内的所有项目的对齐方式。它影响的是所有项作为一个整体如何在容器内分配空间。
参考链接:
CSS3最强布局-Grid布局
css【详解】grid布局—— 网格布局(栅格布局)