สร้าง UI แบบ Masonry ด้วย Flexbox, Multi-Columns Layout และ CSS Grid Layout

Nuttavut Thongjor

Masonry ศัพท์ทางวิศวกรรมที่หมายถึงการก่อสร้างด้วยอิฐ หากเราพูดถึง Masonry Wall ความหมายจึงเป็นกำแพงที่สร้างจากอิฐนั่นเอง

เอกลักษณ์อย่างหนึ่งของกำแพงอิฐคือการเรียงสลับกันไปมาอย่างสวยงาม จึงไม่แปลกที่เราจะเห็นรูปแบบการเรียงตัวเช่นนี้ในหลายเว็บ เช่น Pinterest เป็นต้น

Masonry Wall

การสร้างรูปแบบ Masonry สามารถใช้ JavaScript ได้ แต่ก็ไม่ยากเช่นกันที่จะสำเร็จด้วยความสามารถของ CSS อย่างเดียว สำหรับบทความนี้เราจะมาใช้ Flexbox, CSS Multiple Columns Layout และ CSS Grid Layout เพื่อสร้างความสามารถนี้ให้ปรากฎในหน้าเว็บของเรา

ก่ออิฐแนวนอนด้วย Horizontal Masonry

เริ่มจากขั้นตอนง่าย ๆ ก่อน ด้วยการเรียงอิฐแบบที่คนงานก่อสร้างทำกัน เราจะเรียงอิฐจากซ้ายไปขวาเรื่อย ๆ เพียงแต่อิฐของเรามีขนาดไม่เท่ากันในทุก ๆ ก้อน เรียงไปเรียงมาก็เลยออกมาในรูปแบบนี้

Horizontal Masonry

เริ่มแรกเราต้องมีส่วนของ HTML ซะก่อน โดยแต่ละ box ถือว่าเป็นอิฐแต่ละก้อน ส่วนขนาดของอิฐและการวางตัวนั้น เราจะใช้ CSS เป็นเครื่องกำหนดครับ

HTML
1<div class="container">
2 <div class="box"></div>
3 <div class="box"></div>
4 <div class="box"></div>
5 <div class="box"></div>
6 <div class="box"></div>
7 <div class="box"></div>
8 <div class="box"></div>
9 <div class="box"></div>
10 <div class="box"></div>
11 <div class="box"></div>
12 <div class="box"></div>
13 <div class="box"></div>
14 <div class="box"></div>
15 <div class="box"></div>
16 <div class="box"></div>
17 <div class="box"></div>
18 <div class="box"></div>
19 <div class="box"></div>
20 <div class="box"></div>
21 <div class="box"></div>
22 <div class="box"></div>
23 <div class="box"></div>
24 <div class="box"></div>
25 <div class="box"></div>
26 <div class="box"></div>
27 <div class="box"></div>
28 <div class="box"></div>
29 <div class="box"></div>
30 <div class="box"></div>
31 <div class="box"></div>
32</div>

การวางกล่องนั้นเราวางไปตามแถวต่าง ๆ โดยเริ่มวางจากซ้ายไปขวาและเมื่อสุดขอบของแต่ละแถวให้ทำการขึ้นแถวใหม่ ตรงจุดนี้เราจะใช้ flex-wrap เพื่อปัดอิฐก้อนถัดไปให้ตกไปแถวใหม่แทนที่จะต่อแถวยาวเฟื้อยทะลุขอบจักรวาล

Horizontal Wrap

CSS
1.container {
2 counter-reset: box;
3 display: flex;
4 flex-wrap: wrap;
5 max-width: 1080px;
6 margin: 0 auto;
7 border: 1px dashed #aab2bd;
8}

เพื่อไม่ให้อิฐดูพิการเราจึงกำหนดความสูงให้กับมันไปเลย

CSS
1.box {
2 height: 100px;
3 flex: 1 1 auto;
4}

เพราะเราสั่งอิฐมาจากต่างโรงงานกันขนาดจึงต่างกันโดยสิ้นเชิง อิฐหมายเลข 1, 4, 8, ... จะมีขนาด 50px และมีขนาดเป็น 200px ถ้าเป็นอิฐหมายเลข 2, 6, 10, ... โดยอิฐหมายเลข 3, 7, 11, ... ขนาดเป็น 150px ส่วนสุดท้ายคืออิฐหมายเลข 4, 8, 12, ... จะมีขนาด 100px เราเขียนเงื่อนไขทั้งหมดได้ด้วย CSS ดังนี้

CSS
1// อิฐหมายเลข 1, 4, 8, ...
2.box:nth-child(4n + 1) {
3 width: 50px;
4 background-color: #48cfad;
5}
6
7// อิฐหมายเลข 2, 6, 10, ...
8.box:nth-child(4n + 2) {
9 width: 200px;
10 background-color: #4fc1e9;
11}
12
13// อิฐหมายเลข 3, 7, 11, ...
14.box:nth-child(4n + 3) {
15 width: 150px;
16 background-color: #ed5565;
17}
18
19// อิฐหมายเลข 4, 8, 12, ...
20.box:nth-child(4n + 4) {
21 width: 100px;
22 background-color: #ac92ec;
23}

เกือบสมบูรณ์แล้วหละ ส่วนสุดท้ายที่เราจะทำคือการใส่ตัวเลขเข้าไปในอิฐแต่ละก้อนด้วย CSS ก้อนนี้

CSS
1.container {
2 // reset ค่า counter โดย box จะเป็นตัวนีบเพื่อทำการเพิ่มค่าในแต่ละรอบ
3 counter-reset: box;
4 // ...
5}
6
7.box {
8 // ...
9 position: relative;
10}
11
12.box::before {
13 // เพื่มค่าตัวเลขในแต่ละกล่อง
14 counter-increment: box;
15 content: counter(box);
16 color: #fff;
17 // จัดตำแหน่งตัวเลข
18 position: absolute;
19 left: calc(50% - 10px);
20 top: calc(50% - 8px);
21 line-height: 18px;
22 width: 20px;
23 text-align: center;
24}

เมื่อทุกอย่างเสร็จสิ้นเราจะได้ผลลัพธ์เป็นการเรียงอิฐลักษณะเช่นนี้

Horizontal Masonry

หากเพื่อน ๆ คนไหนต้องการดูผลลัพธ์ด้วยตาตัวเองสามารถคัดลอกโค้ดข้างล่างนี้ไปทดสอบดูได้ครับ

HTML
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <meta http-equiv="X-UA-Compatible" content="ie=edge" />
7 <title>Horizontal Masonry</title>
8 <style>
9 *,
10 ::before,
11 ::after {
12 box-sizing: border-box;
13 }
14
15 .container {
16 counter-reset: box;
17 display: flex;
18 flex-wrap: wrap;
19 max-width: 1080px;
20 margin: 0 auto;
21 border: 1px dashed #aab2bd;
22 }
23
24 .box {
25 height: 100px;
26 flex: 1 1 auto;
27 position: relative;
28 border: 2px solid #ffce54;
29 }
30
31 .box::before {
32 counter-increment: box;
33 content: counter(box);
34 color: #fff;
35 position: absolute;
36 left: calc(50% - 10px);
37 top: calc(50% - 8px);
38 line-height: 18px;
39 width: 20px;
40 text-align: center;
41 }
42
43 .box:nth-child(4n + 1) {
44 width: 50px;
45 background-color: #48cfad;
46 }
47
48 .box:nth-child(4n + 2) {
49 width: 200px;
50 background-color: #4fc1e9;
51 }
52
53 .box:nth-child(4n + 3) {
54 width: 150px;
55 background-color: #ed5565;
56 }
57
58 .box:nth-child(4n + 4) {
59 width: 100px;
60 background-color: #ac92ec;
61 }
62 </style>
63 </head>
64
65 <body>
66 <div class="container">
67 <div class="box"></div>
68 <div class="box"></div>
69 <div class="box"></div>
70 <div class="box"></div>
71 <div class="box"></div>
72 <div class="box"></div>
73 <div class="box"></div>
74 <div class="box"></div>
75 <div class="box"></div>
76 <div class="box"></div>
77 <div class="box"></div>
78 <div class="box"></div>
79 <div class="box"></div>
80 <div class="box"></div>
81 <div class="box"></div>
82 <div class="box"></div>
83 <div class="box"></div>
84 <div class="box"></div>
85 <div class="box"></div>
86 <div class="box"></div>
87 <div class="box"></div>
88 <div class="box"></div>
89 <div class="box"></div>
90 <div class="box"></div>
91 <div class="box"></div>
92 <div class="box"></div>
93 <div class="box"></div>
94 <div class="box"></div>
95 <div class="box"></div>
96 <div class="box"></div>
97 </div>
98 </body>
99</html>

ก่ออิฐแนวตั้งด้วย Vertical Masonry ผ่าน Flexbox

บางครั้งการเรียงกล่องตามแนวนอนให้มีความสูงเท่ากันก็เป็นเรื่องที่น่าเบื่อ โดยเฉพาะกับเว็บที่เน้นรูปภาพ เมื่อแต่ละภาพมีความสูงไม่เท่ากัน ถ้าเรายังดื้อดึงให้ทุกภาพมีความสูงเท่ากันด้วยการทำ Horizontal Masonry สัดส่วนของภาพก็จะเสียไปทันที ทางออกของ Masonry ที่ดีกว่าจึงเป็นการวางอิฐตามแนวตั้งหรือก็คือการทำ Vertical Masonry นั่นเอง

Vertical Masonry

ยังจำกันได้ไหมครับ ถ้าการเรียงอิฐตามแนวนอน Flexbox จะทำให้อิฐทุกก้อนมีความสูงเท่ากันหมด แน่นอนว่าถ้าเรายังคงใช้ flex-direction: row อิฐก้อนที่ 1 กับก้อนที่ 6 จะเป็นแบบในรูปไม่ได้ เพราะการวางตัวแบบนั้น 1 และ 6 จะต้องสูงเท่ากัน

เพื่อให้ข้อจำกัดด้านความสูงไม่เป็นอุปสรรคในการสร้าง Masonry เราจึงจำเป็นต้องเรียงอิฐตามแนวดิ่งด้วยการตั้งค่า flex-direction: column

Masonry Column Flow

CSS
1.container {
2 counter-reset: box;
3 display: flex;
4 // wrap หมายถึง หากไปล่างสุดของแถวแล้ว อิฐก้อนถัดไปให้ขึ้นแถวใหม่
5 flex-flow: column wrap;
6 max-width: 1080px;
7 // จำเป็นต้องตั้งค่าความสูง ไม่เช่นนั้นอิฐจะเรียงยาวลงไปอยู่แค่แถวเดียว
8 max-height: 100vh;
9 margin: 0 auto;
10 border: 1px dashed #aab2bd;
11}
12
13.box {
14 // ตั้งใจให้เรียงอิฐแค่ 3 ก้อนใน 1 แถว
15 // จึงกำหนดให้อิฐแต่ละก้อนมีความกว้าง 33.3% เมื่อครบสามก้อนจะเป็น 100%
16 width: 33.333%;
17 flex: 0 0 auto;
18 // ...
19}

ทุกอย่างก็เหมือนจะไปได้สวย แต่... หลังจากลองดูผลลัพธ์แล้วอุบาทมาก

Vertical Masonry Problem

จากภาพพบว่าแม้เราต้องการจะให้มีผลลัพธ์เกิดขึ้นแค่สามคอลัมน์ แต่ความจริงนั้นไซร์คือล้นกรอบหนักมาก นั้นเพราะเราตั้งความสูงของ Flex Container ไว้ที่ 100vh อิฐจึงไม่สามารถวางซ้อนได้เกินกว่านี้แล้ว เมื่อขึ้นคอลัมน์ใหม่ไปเรื่อย ๆ จึงเป็นผลให้เกิดการทะลุจอดังภาพ

การไม่กำหนดความสูงให้ Flex Container แม้จะดูเหมือนดี แต่มันก็สร้างอีกปัญหา เมื่อความสูงไม่กำหนดไม่ว่าชาตินี้หรือชาติหน้าคอลัมน์ที่สองและสามจะไม่มีทางเกิดขึ้นเลย นั่นเพราะทุกอย่างเรียงยาวไปยาวไป และจบในคอลัมน์แรกแล้ว

อะ ๆ เอาโค้ดทั้งหมดไปรันให้ช้ำใจอีกรอบก่อนไปสู่วิิธีแก้ปัญหาในหัวข้อถัดไปครับ

HTML
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <meta http-equiv="X-UA-Compatible" content="ie=edge" />
7 <title>Vertical Masonry</title>
8 <style>
9 *,
10 ::before,
11 ::after {
12 box-sizing: border-box;
13 }
14
15 .container {
16 counter-reset: box;
17 display: flex;
18 flex-flow: column wrap;
19 max-width: 1080px;
20 max-height: 100vh;
21 margin: 0 auto;
22 border: 1px dashed #aab2bd;
23 }
24
25 .box {
26 width: 33.333%;
27 flex: 0 0 auto;
28 position: relative;
29 border: 2px solid #ffce54;
30 }
31
32 .box::before {
33 counter-increment: box;
34 content: counter(box);
35 color: #fff;
36 position: absolute;
37 left: calc(50% - 10px);
38 top: calc(50% - 8px);
39 line-height: 18px;
40 width: 20px;
41 text-align: center;
42 }
43
44 .box:nth-child(4n + 1) {
45 min-height: 50px;
46 background-color: #48cfad;
47 }
48
49 .box:nth-child(4n + 2) {
50 min-height: 200px;
51 background-color: #4fc1e9;
52 }
53
54 .box:nth-child(4n + 3) {
55 min-height: 150px;
56 background-color: #ed5565;
57 }
58
59 .box:nth-child(4n + 4) {
60 min-height: 100px;
61 background-color: #ac92ec;
62 }
63 </style>
64 </head>
65
66 <body>
67 <div class="container">
68 <div class="box"></div>
69 <div class="box"></div>
70 <div class="box"></div>
71 <div class="box"></div>
72 <div class="box"></div>
73 <div class="box"></div>
74 <div class="box"></div>
75 <div class="box"></div>
76 <div class="box"></div>
77 <div class="box"></div>
78 <div class="box"></div>
79 <div class="box"></div>
80 <div class="box"></div>
81 <div class="box"></div>
82 <div class="box"></div>
83 <div class="box"></div>
84 <div class="box"></div>
85 <div class="box"></div>
86 <div class="box"></div>
87 <div class="box"></div>
88 <div class="box"></div>
89 <div class="box"></div>
90 <div class="box"></div>
91 <div class="box"></div>
92 <div class="box"></div>
93 <div class="box"></div>
94 <div class="box"></div>
95 <div class="box"></div>
96 <div class="box"></div>
97 <div class="box"></div>
98 </div>
99 </body>
100</html>

ก่ออิฐแนวตั้งด้วย Vertical Masonry ผ่าน CSS Multi-column Layout

CSS Multi-column Layout เป็นส่วนหนึงของมาตรฐาน CSS ที่ทำให้เราสามารถสร้างคอลัมน์ขึ้นมาตามจำนวนที่กำหนด และควบคุมการไหลของข้อมูลให้ไปตามคอลัมน์ต่าง ๆ ได้ เมื่อเราประยุกต์ใช้คุณสมบัตินี้กับ Masonry ผลลัพธ์ที่ได้ควรเป็นเช่นนี้

Column Masonry

เริ่มแรกเราต้องการสร้าง Masonry แบบสามคอลัมน์โดยห้ามมีช่องว่าง (gap) ระหว่างคอลัมน์ .container ของเราหน้าตาจึงเป็นเช่นนี้

CSS
1.container {
2 // ...
3 column-count: 3;
4 column-gap: 0;
5 max-width: 1080px;
6 margin: 0 auto;
7 border: 1px dashed #aab2bd;
8}

จากโค้ดข้างต้นสังเกตได้ว่าเรามิได้กำหนดความสูงของ .container เลย เจ้าตัวคอลัมน์จะพยายามแบ่งความสูงแบบเฉลี่ย ๆ กันไป โดยไม่มีข้อมูลล้นกรอบแบบการใช้ Flexbox อีกแล้ว

แต่ทว่ายังมีอีกปัญหาที่อาจเกิดขึ้นได้ เมื่อเนื้อหาของทั้งสามคอลัมน์ถูกเฉลี่ยให้เท่า ๆ กัน หากอิฐที่อยู่ล่างสุดในแต่ละคอลัมน์ยาวเกินไป เบราเซอร์ก็จะทำการหั่นเนื้อหาไปขึ้นคอลัมน์ใหม่ ดังนี้

Column Masonry Problem

ปัญหานี้สามารถแก้ได้ง่ายมากด้วยการบอกเบราเซอร์ว่า ได้โปรดเถอะข้าขอร้อง อย่าหั่นอิฐราคาแพงของข้าเป็นสองท่อนเลยด้วย break-inside: avoid

CSS
1.box {
2 // ...
3 break-inside: avoid;
4}

เมื่อทุกอย่างเสร็จสิ้นเราก็จะได้ผลลัพธ์อันสุดยอดแบบนี้

Column Masonry

และนี่คือโค้ดทั้งหมดของเราที่สร้าง Vertical Masonry ด้วย CSS Multi-column Layout

HTML
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <meta http-equiv="X-UA-Compatible" content="ie=edge" />
7 <title>Vertical Masonry</title>
8 <style>
9 *,
10 ::before,
11 ::after {
12 box-sizing: border-box;
13 }
14
15 .container {
16 counter-reset: box;
17 column-count: 3;
18 column-gap: 0;
19 max-width: 1080px;
20 margin: 0 auto;
21 border: 1px dashed #aab2bd;
22 }
23
24 .box {
25 position: relative;
26 border: 2px solid #ffce54;
27 break-inside: avoid;
28 }
29
30 .box::before {
31 counter-increment: box;
32 content: counter(box);
33 color: #fff;
34 position: absolute;
35 left: calc(50% - 10px);
36 top: calc(50% - 8px);
37 line-height: 18px;
38 width: 20px;
39 text-align: center;
40 }
41
42 .box:nth-child(4n + 1) {
43 min-height: 100px;
44 background-color: #48cfad;
45 }
46
47 .box:nth-child(4n + 2) {
48 min-height: 200px;
49 background-color: #4fc1e9;
50 }
51
52 .box:nth-child(4n + 3) {
53 min-height: 150px;
54 background-color: #ed5565;
55 }
56
57 .box:nth-child(4n + 4) {
58 min-height: 250px;
59 background-color: #ac92ec;
60 }
61 </style>
62 </head>
63
64 <body>
65 <div class="container">
66 <div class="box"></div>
67 <div class="box"></div>
68 <div class="box"></div>
69 <div class="box"></div>
70 <div class="box"></div>
71 <div class="box"></div>
72 <div class="box"></div>
73 <div class="box"></div>
74 <div class="box"></div>
75 <div class="box"></div>
76 <div class="box"></div>
77 <div class="box"></div>
78 <div class="box"></div>
79 <div class="box"></div>
80 <div class="box"></div>
81 <div class="box"></div>
82 <div class="box"></div>
83 <div class="box"></div>
84 <div class="box"></div>
85 <div class="box"></div>
86 <div class="box"></div>
87 <div class="box"></div>
88 <div class="box"></div>
89 <div class="box"></div>
90 <div class="box"></div>
91 <div class="box"></div>
92 <div class="box"></div>
93 <div class="box"></div>
94 <div class="box"></div>
95 <div class="box"></div>
96 </div>
97 </body>
98</html>

CSS Multi-column Layout ช่วยให้การทำ Verticla Masonry เป็นเรื่องง่ายพอ ๆ กับที่เราสร้าง Horizontal Masonry ได้อย่างง่ายด้วย Flexbox แต่นั่นก็ยังไม่ช่วยให้ปัญหาของเราหมดไป ลองจินตนาการดูครับหากความต้องการของเราคือมีอิฐบางก้อนใหญ่ชนิดข้ามแถวหรือข้ามคอลัมน์ได้ แบบนี้จะทำอย่างไร?

Grid Masonry

สร้าง Masonry อย่างสมบูรณ์แบบด้วย CSS Grid Layout

เราอธิบายเรื่องความหมายและการใช้งาน CSS Grid Layout กันไปแล้ว ดังนั้นผมจะไม่กล่าวซ้ำอีกครั้งในบทความนี้ แต่เราจะเสริมบางสิ่งที่ขาดหายไปเพื่อเติมเต็มให้ Masonry มีความสมบูรณ์

สิ่งแรกที่เราต้องการให้ Grid ตัดสินใจคือจำนวนคอลัมน์สำหรับ Masonry วิธีการของเราคือสั่งให้ Grid พิจารณาเองด้วยการสร้างจำนวนคอลัมน์ให้พอเหมาะตามเงื่อนไขที่ว่า แต่ละคอลัมน์จะมีความกว้างได้ไม่ต่ำกว่า 200px และความกว้างสูงสุดคือ 1fr คำสั่งนี้ให้ทำการสร้างคอลัมน์ไปเรื่อย ๆ (repeat) จนกว่าจะเต็มพื้นที่แสดงผล

CSS
1.container {
2 display: grid;
3 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
4 // ...
5}

เมื่อเราบอกว่า Grid จะสร้าง tracks ขึ้นมาเองตามความเป็นไปได้ เราจึงควรกำหนดด้วยว่าเมื่อ Grid สร้างแต่ละแถวขึ้นมาจะให้ tracks ของแต่ละแถวมีความสูงเท่าไร ในที่นี้คือ 1fr หรือก็คือให้ทุกแถวมีความสูงเท่ากัน

CSS
1.container {
2 grid-auto-rows: 1fr;
3 // ...
4}

ถึงตอนนี้โค้ดเต็ม ๆ ของเราจะเป็นดังนี้ครับ

HTML
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <meta http-equiv="X-UA-Compatible" content="ie=edge" />
7 <title>Grid Masonry</title>
8 <style>
9 *,
10 ::before,
11 ::after {
12 box-sizing: border-box;
13 }
14
15 .container {
16 counter-reset: box;
17 display: grid;
18 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
19 grid-auto-rows: 1fr;
20 max-width: 1080px;
21 height: 100vh;
22 margin: 0 auto;
23 border: 1px dashed #aab2bd;
24 }
25
26 .box {
27 position: relative;
28 border: 2px solid #ffce54;
29 }
30
31 .box::before {
32 counter-increment: box;
33 content: counter(box);
34 color: #fff;
35 position: absolute;
36 left: calc(50% - 10px);
37 top: calc(50% - 8px);
38 line-height: 18px;
39 width: 20px;
40 text-align: center;
41 }
42
43 .box:nth-child(4n + 1) {
44 background-color: #48cfad;
45 }
46
47 .box:nth-child(4n + 2) {
48 grid-row: span 2;
49 grid-column: span 2;
50 background-color: #4fc1e9;
51 }
52
53 .box:nth-child(4n + 3) {
54 background-color: #ed5565;
55 }
56
57 .box:nth-child(4n + 4) {
58 grid-row: span 2;
59 grid-column: span 2;
60 background-color: #ac92ec;
61 }
62 </style>
63 </head>
64
65 <body>
66 <div class="container">
67 <div class="box"></div>
68 <div class="box"></div>
69 <div class="box"></div>
70 <div class="box"></div>
71 <div class="box"></div>
72 <div class="box"></div>
73 <div class="box"></div>
74 <div class="box"></div>
75 <div class="box"></div>
76 <div class="box"></div>
77 <div class="box"></div>
78 </div>
79 </body>
80</html>

เมื่อทดสอบโปรแกรมเราจะได้ผลลัพธ์อันไม่น่าพึงพอใจ ดังนี้

Grid Masonry Problem

คุณหลอกดาว ทำไม Masonry ของเราช่างมีรูแหว่งอย่างกับคนฟันหลอเยี่ยงนี้

CSS Grid Layout มีหลักการที่เรียกว่า Auto-Placement หลักการดังกล่าวเป็นตัวช่วยในการตัดสินใจว่า เมื่อเราไม่ทำการระบุตำแหน่งการวางของ Grid Item จะให้ของชิ้นนั้นวางตำแหน่งไหนดี เราสามารถกำหนดให้เบราเซอร์ทำการสแกนหาช่องวางในแนวแถวเพื่ออุดฟันหลอด้วยการวาง Grid Item ที่พอเหมาะผ่าน grid-auto-flow: dense ดังนี้

CSS
1.container {
2 grid-auto-flow: dense;
3 // ...
4}

เพียงเท่านี้เราก็จะได้ Masonry สวยงามตามแบบฉบับของ CSS Grid Layout

Grid Masonry

สิ่งหนึ่งที่ต้องพึงระวังเป็นพิิเศษคือ เมื่อเราใช้ grid-auto-flow: dense Grid Item ของเราอาจไม่ได้เรียงตามลำดับอีกต่อไปครับ

แสดงรูปภาพแบบ Masonry ด้วย CSS Grid Layout

หลังจากผ่านทฤษฎีมาเยอะแล้ว เราลองนำหลักการจากตัวอย่างที่แล้วมาเปลี่ยนเป็นรูปภาพกันครับ ตัวอย่างภาพและโค้ดของเราจะเป็นดังนี้ ไม่อธิบายเยอะแล้วเนอะ เจ็บขี้ฟัน

Grid Image Masonry

HTML
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <meta http-equiv="X-UA-Compatible" content="ie=edge" />
7 <title>Masonry</title>
8 <style>
9 *,
10 ::before,
11 ::after {
12 box-sizing: border-box;
13 }
14
15 .container {
16 counter-reset: box;
17 display: grid;
18 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
19 grid-auto-rows: 1fr;
20 grid-auto-flow: dense;
21 grid-gap: 5px;
22 max-width: 1080px;
23 height: 100vh;
24 margin: 0 auto;
25 }
26
27 .box {
28 margin: 0;
29 display: flex;
30 flex-direction: column;
31 }
32
33 .box > img {
34 flex: 1;
35 max-width: 100%;
36 object-fit: cover;
37 }
38
39 .box > figcaption {
40 padding: 0.5rem 1rem;
41 background-color: #48cfad;
42 color: #fff;
43 text-align: center;
44 }
45
46 .box:nth-child(2n + 1) {
47 grid-row: span 2;
48 grid-column: span 2;
49 }
50 </style>
51 </head>
52
53 <body>
54 <div class="container">
55 <figure class="box">
56 <img src="https://source.unsplash.com/collection/311028" />
57 <figcaption>Autumn</figcaption>
58 </figure>
59 <figure class="box">
60 <img src="https://source.unsplash.com/collection/395888" />
61 <figcaption>Halloween</figcaption>
62 </figure>
63 <figure class="box">
64 <img src="https://source.unsplash.com/collection/1999207" />
65 <figcaption>Political</figcaption>
66 </figure>
67 <figure class="box">
68 <img src="https://source.unsplash.com/collection/827807" />
69 <figcaption>Technic</figcaption>
70 </figure>
71 <figure class="box">
72 <img src="https://source.unsplash.com/collection/1538150" />
73 <figcaption>Milkyway</figcaption>
74 </figure>
75 <figure class="box">
76 <img src="https://source.unsplash.com/collection/1424240" />
77 <figcaption>Animals</figcaption>
78 </figure>
79 <figure class="box">
80 <img src="https://source.unsplash.com/collection/1767181" />
81 <figcaption>Blancs</figcaption>
82 </figure>
83 </div>
84 </body>
85</html>

สรุป

การสร้าง Masonry ด้วยเทคนิคต่าง ๆ ของ CSS นั้นไม่ใช่เรื่องยากครับ แต่ละเทคนิคก็มีความยากง่ายต่างกัน หากเราต้องการสร้าง Horizontal Masonry การใช้งาน Flexbox ก็จะดูง่าย แต่หากต้องการสร้าง Vertical Masonry แทน การใช้งาน CSS Multi-columns Layout ดูเหมือนจะเป็นทางออกมากกว่า และเมื่อไหร่ที่เราต้องการให้แต่ละกล่องของเราข้ามไปมาระหว่างแถวและคอลัมน์ได้ CSS Grid Layout ถือเป็นตัวช่วยที่ดีที่สุด

สารบัญ

สารบัญ

  • ก่ออิฐแนวนอนด้วย Horizontal Masonry
  • ก่ออิฐแนวตั้งด้วย Vertical Masonry ผ่าน Flexbox
  • ก่ออิฐแนวตั้งด้วย Vertical Masonry ผ่าน CSS Multi-column Layout
  • สร้าง Masonry อย่างสมบูรณ์แบบด้วย CSS Grid Layout
  • แสดงรูปภาพแบบ Masonry ด้วย CSS Grid Layout
  • สรุป