瀑布流布局实现

JS实现

首先 https://github.com/desandro/masonry 是一个很好的插件,是一个比较不错的方案,使用的是绝对定位。

npm 使用方法,其他使用方法查看官网

1
npm i -D masonry-layout
1
2
3
4
5
<div class="grid">
<div class="grid-item"></div>
<div class="grid-item grid-item--height1"></div>
<div class="grid-item grid-item--height2"></div>
</div>
1
2
3
4
5
6
7
8
9
.grid-item {
float: left;
width: 200px;
height: 60px;
border: 2px solid hsla(0, 0%, 0%, 0.5);
}

.grid-item--height1 { height: 160px; }
.grid-item--height2 { height: 140px; }
1
2
3
4
5
6
import Masonry from 'masonry-layout'

const msnry = new Masonry('.grid', {
itemSelector: '.grid-item',
columnWidth: 200,
})

此代码库通过https://segmentfault.com/a/1190000013675077 找到

除了 https://github.com/desandro/masonry 还有 https://github.com/metafizzy/isotope ,用法都很简单。

CSS实现

不管是使用float还是inline-block布局都无法很好的控制列表项目堆栈之间的间距。最终得到的效果就像下面这样:

033

css实现从 https://www.w3cplus.com/css/pure-css-create-masonry-layout.html 处学习到,分三种Multi-columnsFlexboxGrid,之所以为选择这三种方式,因为对这三种属性在手机端的兼容性的不了解,所以未使用此方案。下面展示这几种的使用方法,顺便加入一点自己的理解。

瀑布流布局有一个专业的英文名称Masonry Layouts。瀑布流布局已经有好多年的历史了,我最早知道这个名词的时候大约是在2012年,当时Pinterest网站的布局就是使用的这种流式布局,简言之像Pinterest网站这样的布局就称之为瀑布流布局,也有人称之为Pinterest 布局。

瀑布流布局其核心是基于一个网格的布局,而且每行包含的项目列表高度是随机的(随着自己内容动态变化高度),同时每个项目列表呈堆栈形式排列,最为关键的是,堆栈之间彼此之间没有多余的间距差存大

Multi-columns

首先最早尝试使用纯CSS方法解决瀑布流布局的是CSS3 的Multi-columns。其最早只是用来用来实现文本多列排列(类似报纸杂志样的文本排列)。但对于前端同学来说,他们都是非常具有创意和创新的,有人尝试通过Multi-columns相关的属性column-countcolumn-gap配合break-inside来实现瀑布流布局。

比如我们有一个类似这样的HTML结构:

1
2
3
4
5
6
7
8
9
10
11
<div class="masonry">
<div class="item">
<div class="item__content">
</div>
</div>
<div class="item">
<div class="item__content">
</div>
</div>
<!-- more items -->
</div>

其中div.masonry是瀑布流的容器,其里面放置了n个列表div.item。为了节约篇幅,上面代码仅列了两个。结构有了,现在来看CSS。在.masonry中设置column-countcolumn-gap,前者用来设置列数,后者设置列间距:

1
2
3
4
.masonry {
column-count: 5;
column-gap: 0;
}

上面控制了列与列之间的效果,但这并不是最关键之处。当初纯CSS实现瀑布流布局中最关键的是堆栈之间的间距,而并非列与列之间的控制(说句实话,列与列之间的控制float之类的就能很好的实现)。找到实现痛楚,那就好办了。或许你会问有什么CSS方法可以解决这个。在CSS中有一个break-inside属性,这个属性也是实现瀑布流布局最关键的属性。

1
2
3
4
5
.item {
break-inside: avoid;
box-sizing: border-box;
padding: 10px;
}

其中break-inside:avoid为了控制文本块分解成单独的列,以免项目列表的内容跨列,破坏整体的布局。当然为了布局具有响应式效果,可以借助媒体查询属性,在不同的条件下使用column-count设置不同的列,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.masonry {
column-count: 1; // one column on mobile
}
@media (min-width: 400px) {
.masonry {
column-count: 2; // two columns on larger phones
}
}
@media (min-width: 1200px) {
.masonry {
column-count: 3; // three columns on...you get it
}
}
<!-- etc. -->

下面是完整例子和效果图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<div class="masonry">
<div class="item">
<div class="item__content">
</div>
</div>
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
<div class="item">
<div class="item__content item__content--medium">
</div>
</div>
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
<div class="item">
<div class="item__content item__content--medium">
</div>
</div>
<div class="item">
<div class="item__content">
</div>
</div>
<div class="item">
<div class="item__content item__content--large">
</div>
</div>
<div class="item">
<div class="item__content item__content--medium">
</div>
</div>
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
<div class="item">
<div class="item__content">
</div>
</div>
<div class="item">
<div class="item__content item__content--large">
</div>
</div>
<div class="item">
<div class="item__content">
</div>
</div>
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
<div class="item">
<div class="item__content item__content--large">
</div>
</div>
<div class="item">
<div class="item__content item__content--medium">
</div>
</div>
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
<div class="item">
<div class="item__content item__content--medium">
</div>
</div>
<div class="item">
<div class="item__content">
</div>
</div>
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
</div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
@import url('https://fonts.googleapis.com/css?family=PT+Mono');

$bg: #4F000B;
$itemBg1: #720026;
$itemBg2: #CE4257;
$itemBg3: #FFC093;
$itemBg4: #FF7F51;
$counterBg: #222;

@mixin setColorAndHover($baseColor) {
color: $baseColor;
&:hover {
background: lighten($baseColor, 8%);
}
}

body,
html {
position: relative;
width: 100%;
height: 100%;
background: $bg;
font-family: "PT Mono", monospace;
}

.masonry {
column-count: 1;
column-gap: 0;
counter-reset: item-counter;

@media screen and (min-width: 400px) {
column-count: 2;
}

@media screen and (min-width: 600px) {
column-count: 3;
}

@media screen and (min-width: 800px) {
column-count: 4;
}

@media screen and (min-width: 1100px) {
column-count: 5;
}
}

.item {
box-sizing: border-box;
break-inside: avoid;
padding: 10px;
counter-increment: item-counter;

&__content {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 220px;
font-size: 40px;
color: darken($bg, 5%);
background: currentColor;
box-sizing: border-box;
@include setColorAndHover($itemBg1);

&:before {
position: absolute;
top: 0;
left: 0;
font-size: 13px;
width: 2em;
height: 2em;
line-height: 2em;
text-align: center;
font-weight: bold;
background-color: $counterBg;
content: counter(item-counter);
}

&:after {
color: darken($bg, 10%);
content: 'ಠ‿ಠ';
}

&--small {
@include setColorAndHover($itemBg2);
height: 100px;

&:after {
content: '♥◡♥';
}
}

&--medium {
@include setColorAndHover($itemBg3);
height: 175px;

&:after {
content: '◔ᴗ◔';
}
}

&--large {
@include setColorAndHover($itemBg4);
height: 280px;

&:after {
content: 'ಠ_๏';
}
}
}
}

034

从结果来看,此方法没有使item水平方向布局

Flexbox

Flexbox布局到今天已经是使用非常广泛的,也算是很成熟的一个特性。那接下来我们就看Flexbox怎么实现瀑布流布局。如果你从未接触过Flexbox相关的属性,那建议你点击这里阅读。如果你觉得这里信息量过于太多,那强列建议你阅读下面几篇文章,阅读完之后你对Flexbox相关属性会有一个彻底的了解:

一个完整的Flexbox指南
图解CSS3 Flexbox属性
理解Flexbox:你需要知道的一切
终极Flexbox属性查询列表
Flexbox布局实战
深入理解 flex 布局以及计算

上面这几篇文章告诉了你有关于Flexbox的一切:

接下来回到我们今天的正题当中,使用Flexbox实现瀑布流布局有两种方案。

一个主要的列容器

结构依旧和Multi-columns小节中展示的一样。只是在.masonry容器中使用的CSS不一样:

1
2
3
4
5
6
.masonry {
display: flex;
flex-flow: column wrap;
width: 100%;
height: 800px;
}

之前在.masonry中是通过column-count来控制列,这里采用flex-flow来控制列,并且允许它换行。这里关键是容器的高度,示例中显式的设置了height属性,当然除了设置px值,还可以设置100vh,让.masonry容器的高度和浏览器视窗高度一样。记住,这里height可以设置成任何高度值(采用任何的单位),但不能不显式的设置,如果没有显式的设置,容器就无法包裹住项目列表。

使用Flexbox布局,对于.item可以不再使用break-inside:avoid,但其它属性可以是一样。同样的,响应式设置,使用Flexbox实现响应式布局比多列布局要来得容易,他天生就具备这方面的能力,只不过我们这里需要对容器的高度做相关的处理。前面也提到过了,如果不给.masonry容器显式设置高度是无法包裹项目列表的,那么这里响应式设计中就需要在不同的媒体查询条件下设置不同的高度值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.masonry {
height: auto;
}

@media screen and (min-width: 400px) {
.masonry {
height: 1600px;
}
}

@media screen and (min-width: 600px) {
.masonry {
height: 1300px;
}
}

@media screen and (min-width: 800px) {
.masonry {
height: 1100px;
}
}

@media screen and (min-width: 1100px) {
.masonry {
height: 800px;
}
}

同样来看一个示例效果(完整的代码和效果):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<div class="masonry">
<div class="item">
<div class="item__content">
</div>
</div>
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
<div class="item">
<div class="item__content item__content--medium">
</div>
</div>
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
<div class="item">
<div class="item__content item__content--medium">
</div>
</div>
<div class="item">
<div class="item__content">
</div>
</div>
<div class="item">
<div class="item__content item__content--large">
</div>
</div>
<div class="item">
<div class="item__content item__content--medium">
</div>
</div>
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
<div class="item">
<div class="item__content">
</div>
</div>
<div class="item">
<div class="item__content item__content--large">
</div>
</div>
<div class="item">
<div class="item__content">
</div>
</div>
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
<div class="item">
<div class="item__content item__content--large">
</div>
</div>
<div class="item">
<div class="item__content item__content--medium">
</div>
</div>
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
<div class="item">
<div class="item__content item__content--medium">
</div>
</div>
<div class="item">
<div class="item__content">
</div>
</div>
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
</div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
@import url('https://fonts.googleapis.com/css?family=PT+Mono');

@function set-counter-bg-color($color) {
@if (lightness($color) > 50) {
@return darken($color, 60%);
} @else {
@return lighten($color, 50%);
}
}

$bg: #0D0630;
$itemBg1: #18314F;
$itemBg2: #8BBEB2;
$itemBg3: #E6F9AF;
$itemBg4: #384E77;

@mixin setColorAndHover($baseColor) {
color: $baseColor;
&:hover {
background: lighten($baseColor, 8%);
}
}

body,
html {
position: relative;
width: 100%;
height: 100%;
background: $bg;
font-family: "PT Mono", monospace;
}

.masonry {
display: flex;
flex-flow: column wrap;
width: 100%;
height: auto;

@media screen and (min-width: 400px) {
height: 1600px;
}

@media screen and (min-width: 600px) {
height: 1300px;
}

@media screen and (min-width: 800px) {
height: 1100px;
}

@media screen and (min-width: 1100px) {
height: 800px;
}
}

.item {
box-sizing: border-box;
padding: 10px;
counter-increment: item-counter;

&__content {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 220px;
font-size: 40px;
color: darken($bg, 5%);
background: currentColor;
box-sizing: border-box;
@include setColorAndHover($itemBg1);

&:before {
position: absolute;
top: 0;
left: 0;
font-size: 13px;
width: 2em;
height: 2em;
line-height: 2em;
text-align: center;
font-weight: bold;
background-color: set-counter-bg-color($itemBg1);
content: counter(item-counter);
}

&:after {
color: darken($bg, 10%);
content: 'ಠ‿ಠ';
}

&--small {
@include setColorAndHover($itemBg2);
height: 100px;

&:before {
background: set-counter-bg-color($itemBg2);
}

&:after {
content: '♥◡♥';
}
}

&--medium {
@include setColorAndHover($itemBg3);
height: 175px;

&:before {
background: set-counter-bg-color($itemBg3);
}

&:after {
content: '◔ᴗ◔';
}
}

&--large {
@include setColorAndHover($itemBg4);
height: 280px;

&:before {
background: set-counter-bg-color($itemBg4);
}

&:after {
content: 'ಠ_๏';
}
}
}
}

035

这个解决方案有一个最致命的地方,就是需要显式的给.masonry设置height,特别对于响应式设计来说这个更为不友好。而且当我们的项目列表是动态生成,而且内容不好控制之时,这就更蛋疼了。那么有没有更为友好的方案呢?

单独的列容器

这个方案,我们需要对我们的HTML结构做一个变更。变更后的HTML结构看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div class="masonry">
<div class="column">
<div class="item">
<div class="item__content">
</div>
</div>
<!-- more items -->
</div>
<div class="column">
<div class="item">
<div class="item__content">
</div>
</div>
<!-- more items -->
</div>
<div class="column">
<div class="item">
<div class="item__content">
</div>
</div>
<!-- more items -->
</div>
</div>

不难发现,在div.item外面包了一层div.column,这个div.column称为列表项目的单独容器。在这个解决方案中,.masonry.column都通过display:flex属性将其设置为Flex容器,不同的是.masonry设置为行(flex-direction:row),而.column设置为列(flex-direction):

1
2
3
4
5
6
7
8
9
10
.masonry {
display: flex;
flex-direction: row;
}

.column {
display: flex;
flex-direction: column;
width: calc(100%/3);
}

这里有一个需要注意,在.column咱们通过calc()方法来控制每个列的宽度,如果你希望是三列,那么可以设置width: calc(100% / 3);实际中根据自己的设计来设置width

1
2
3
4
5
6
7
8
9
10
.masonry {
display: flex;
flex-direction: row;
}

.column {
display: flex;
flex-direction: column;
width: calc(100%/3);
}

这种方案对应的响应式设计,需要在不同的媒体查询下修改width值,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.masonry {
display: flex;
flex-direction: column;
}

@media only screen and (min-width: 500px) {
.masonry {
flex-direction: row;
}
}

.column {
display: flex;
flex-flow: column wrap;
width: 100%;
}

@media only screen and (min-width: 500px) {
.column {
width: calc(100%/5);
}
}

效果如下(完整代码和效果图):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<div class="masonry">
<div class="column">
<div class="item">
<div class="item__content">
</div>
</div>
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
<div class="item">
<div class="item__content item__content--medium">
</div>
</div>
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
</div>
<div class="column">
<div class="item">
<div class="item__content item__content--medium">
</div>
</div>
<div class="item">
<div class="item__content">
</div>
</div>
<div class="item">
<div class="item__content item__content--large">
</div>
</div>
<div class="item">
<div class="item__content item__content--medium">
</div>
</div>
</div>
<div class="column">
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
<div class="item">
<div class="item__content">
</div>
</div>
<div class="item">
<div class="item__content item__content--large">
</div>
</div>
<div class="item">
<div class="item__content">
</div>
</div>
</div>
<div class="column">
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
<div class="item">
<div class="item__content item__content--large">
</div>
</div>
<div class="item">
<div class="item__content item__content--medium">
</div>
</div>
</div>
<div class="column">
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
<div class="item">
<div class="item__content item__content--medium">
</div>
</div>
<div class="item">
<div class="item__content">
</div>
</div>
<div class="item">
<div class="item__content item__content--small">
</div>
</div>
</div>
</div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
@import url('https://fonts.googleapis.com/css?family=PT+Mono');

@function set-counter-bg-color($color) {
@if (lightness($color) > 50) {
@return darken($color, 60%);
} @else {
@return lighten($color, 50%);
}
}

$bg: #0D0630;
$itemBg1: #18314F;
$itemBg2: #8BBEB2;
$itemBg3: #E6F9AF;
$itemBg4: #384E77;

@mixin setColorAndHover($baseColor) {
color: $baseColor;
&:hover {
background: lighten($baseColor, 8%);
}
}

body,
html {
position: relative;
width: 100%;
height: 100%;
background: $bg;
font-family: "PT Mono", monospace;
}

.masonry {
display: flex;
flex-direction: column;

@media only screen and (min-width: 500px) {
flex-direction: row;
}
}

.column {
display: flex;
flex-flow: column wrap;
width: 100%;

@media only screen and (min-width: 500px) {
width: calc(100%/5);
}
}

.item {
box-sizing: border-box;
padding: 10px;
counter-increment: item-counter;

&__content {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 220px;
font-size: 40px;
color: darken($bg, 5%);
background: currentColor;
box-sizing: border-box;
@include setColorAndHover($itemBg1);

&:before {
position: absolute;
top: 0;
left: 0;
font-size: 13px;
width: 2em;
height: 2em;
line-height: 2em;
text-align: center;
font-weight: bold;
background-color: set-counter-bg-color($itemBg1);
content: counter(item-counter);
}

&:after {
color: darken($bg, 10%);
content: 'ಠ‿ಠ';
}

&--small {
@include setColorAndHover($itemBg2);
height: 100px;

&:before {
background: set-counter-bg-color($itemBg2);
}

&:after {
content: '♥◡♥';
}
}

&--medium {
@include setColorAndHover($itemBg3);
height: 175px;

&:before {
background: set-counter-bg-color($itemBg3);
}

&:after {
content: '◔ᴗ◔';
}
}

&--large {
@include setColorAndHover($itemBg4);
height: 280px;

&:before {
background: set-counter-bg-color($itemBg4);
}

&:after {
content: 'ಠ_๏';
}
}
}
}

036

从实战结果已经告诉你答案了。只不过在结构上变得冗余一点。

这个实现了item的水平排布,但是需要事先手动分数据

Grid

Grid将是布局当中的一把利剑,也可以说是神器,特别是今年得到了众多浏览器的支持。记得去年在CSSConf分享后,有同学问我Grid是否能实现瀑布流的布局。说实话,虽然Grid对于布局而言是非常的强大,但要很好的实现瀑布流布局还是非常的蛋疼。@Rachel Andrew在她 分享的文章中也特意提到过实现瀑布流的方案。从文章中摘出有关于瀑布流布局的那部分内容。

Grid制作瀑布流,对于结构而言和Multi-columns示例中的一样。只不过在.masonry使用display:grid来进行声明:

1
2
3
4
5
6
.masonry {
display: grid;
grid-gap: 40px;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(50px, auto);
}

对于.item较为蛋疼,需要分别通过grid-rowgrid-column来指定列表项目所在的区域,比如:

1
2
3
4
5
6
7
8
9
10
.masonry > div:nth-child(1) {
grid-row: 1 / 4;
grid-column: 1;
}

.masonry > div:nth-child(2) {
grid-row: 1 / 3;
grid-column: 2;
}
...

将效果Fork过来(全部代码和效果图):

1
2
3
4
5
6
7
8
9
<div class="grid">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
</div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
.grid {
margin: 50px;
padding: 20px;
background-color: #e3fafc;
border: 1px solid #99e9f2;
display: grid;
grid-gap: 40px;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(50px, auto);
}

.grid > div {
border: 1px solid #3bc9db;
border-radius: 3px;
background-color: #99e9f2;
font-size:250%;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
}

.grid > div:nth-child(1) {
grid-row: 1 / 4;
grid-column: 1;
}

.grid > div:nth-child(2) {
grid-row: 1 / 3;
grid-column: 2;
}

.grid > div:nth-child(3) {
grid-row: 1 / 4;
grid-column: 3;
}

.grid > div:nth-child(4) {
grid-row: 4 / 7;
grid-column: 1;
}

.grid > div:nth-child(5) {
grid-row: 3 / 7;
grid-column: 2;
}

.grid > div:nth-child(6) {
grid-row: 4 / 6;
grid-column: 3;
}

037

在Grid中有自动排列的算法的属性:

  • 如果没有明确指定网格项目位置,网格会按自动排列算法,将它最大化利用可用空间
  • 如果在当前行没有可用位置,网格会自动搜索下一行,这样会造成一定的差距,浪费可用空间
  • 可以把grid-auto-flowrow值改变auto,可以切换搜索顺序
  • grid-auto-flow还可以接受另一个关键词。默认情况下,其值是sparse,但我们可以将其显式的设置为dense,让网格项目试图自动填补所有可用的空白空间

言外之意,Grid中自动排列的算法对于实现瀑布流布局有很大的帮助。不过对于堆栈(列表项目)高度不能友好的控制。

总结

这篇文章主要介绍了如何使用纯CSS实现瀑布流的布局。文章简单介绍了三种实现方案:Multi-columns、Flexbox和Grid。从上面的示例或者实现手段而言,较我友好的是Flexbox的方案。当然,随着CSS Grid特性的完善,使用Grid实现瀑布流布局将会变得更为简单和友好。那让我们拭目以待。当然如果你觉得这些方案都不太好,你可以依旧可以考虑JavaScript的解决方案。如果你有更好的解决方案,也希望能在下面的评论中与我们一起分享。