提问人:Pekka 提问时间:6/6/2017 最后编辑:Temani AfifPekka 更新时间:4/19/2023 访问量:72069
仅限 CSS 的砌体布局
CSS-only masonry layout
问:
我需要实现砌体布局。但是,出于多种原因,我不想使用 JavaScript 来做到这一点。
参数:
- 所有元素的宽度都相同
- 元素的高度无法在服务器端计算(图像加上各种数量的文本)
- 如果有必要,我可以忍受固定数量的列
有一个适用于现代浏览器的简单解决方案,即 column-count
属性。
该解决方案的问题在于元素按列排序:
虽然我需要将元素按行排序,但至少大致如下:
我尝试过但行不通的方法:
现在我可以更改服务器端呈现并重新排序项目,将项目数除以列数,但这很复杂,容易出错(基于浏览器决定将项目列表拆分为列的方式),所以如果可能的话,我想避免它。
有没有一些 flexbox 魔力使这成为可能?
答:
2021年更新
CSS 网格布局级别 3 包括一项功能。masonry
代码将如下所示:
grid-template-rows: masonry
grid-template-columns: masonry
截至 2021 年 3 月,它仅在 Firefox 中可用(激活标志后)。
- https://drafts.csswg.org/css-grid-3/#masonry-layout
- https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Masonry_Layout
结束更新;原文答案如下
弹性盒
使用 flexbox 无法实现动态砌体布局,至少不能以干净有效的方式进行。
Flexbox 是一个一维布局系统。这意味着它可以沿水平线或垂直线对齐项目。Flex 项仅限于其行或列。
真正的网格系统是二维的,这意味着它可以沿着水平线和垂直线对齐项目。内容项可以同时跨行和跨列,这是 Flex 项无法做到的。
这就是为什么 flexbox 构建网格的能力有限的原因。这也是 W3C 开发另一种 CSS3 技术 Grid Layout 的原因。
row wrap
在带有 的 flex 容器中,flex 项必须换行到新行。flex-flow: row wrap
这意味着一个 flex 项目不能换行在同一行中的另一个项目下。
请注意上面的 div #3 如何换行在 div #1 下面,从而创建一个新行。它不能换行在 div #2 下面。
因此,当项目不是行中最高的时,留白会保留,从而产生难看的间隙。
column wrap
如果切换到 ,则更容易实现类似网格的布局。但是,列方向容器立即存在四个潜在问题:flex-flow: column wrap
- Flex 项目是垂直流动的,而不是水平流动的(就像您在本例中需要的那样)。
- 容器是水平扩展的,而不是垂直扩展的(如 Pinterest 布局)。
- 它要求容器具有固定的高度,以便物品知道在哪里包装。
- 在撰写本文时,它在所有主要浏览器中都存在缺陷,其中容器无法扩展以容纳其他列。
因此,在这种情况下,列方向容器不是一个选项,在许多其他情况下也是如此。
未定义项目维度的 CSS 网格
如果可以预先确定内容项的各种高度,网格布局将是解决您问题的完美解决方案。所有其他要求都在 Grid 的能力范围内。
必须知道网格项的宽度和高度,以便缩小与周围项的间隙。
因此,Grid 是构建水平流动砌体布局的最佳 CSS,在这种情况下却不足。
事实上,在CSS技术出现并能够自动缩小差距之前,CSS通常没有解决方案。像这样的事情可能需要重排文档,所以我不确定它会有多大用处或效率。
你需要一个脚本。
JavaScript 解决方案倾向于使用绝对定位,即从文档流中删除内容项,以便重新排列它们,没有间隙。下面是两个示例:
Masonry 是一个 JavaScript 网格布局库。它 工作原理是将元素放置在可用的最佳位置 垂直空间,有点像泥瓦匠在墙上安装石头。
[Pinterest] 确实是一个很酷的网站,但我觉得有趣的是这些钉板是如何布置的......因此,本教程的目的是我们自己重新创建这种响应式块效果......
来源: https://benholland.me/javascript/2012/02/20/how-to-build-a-site-that-works-like-pinterest.html
定义了项目维度的 CSS 网格
对于已知内容项的宽度和高度的布局,下面是纯 CSS 中水平流动的砌体布局:
grid-container {
display: grid; /* 1 */
grid-auto-rows: 50px; /* 2 */
grid-gap: 10px; /* 3 */
grid-template-columns: repeat(auto-fill, minmax(30%, 1fr)); /* 4 */
}
[short] {
grid-row: span 1; /* 5 */
background-color: green;
}
[tall] {
grid-row: span 2;
background-color: crimson;
}
[taller] {
grid-row: span 3;
background-color: blue;
}
[tallest] {
grid-row: span 4;
background-color: gray;
}
grid-item {
display: flex;
align-items: center;
justify-content: center;
font-size: 1.3em;
font-weight: bold;
color: white;
}
<grid-container>
<grid-item short>01</grid-item>
<grid-item short>02</grid-item>
<grid-item tall>03</grid-item>
<grid-item tall>04</grid-item>
<grid-item short>05</grid-item>
<grid-item taller>06</grid-item>
<grid-item short>07</grid-item>
<grid-item tallest>08</grid-item>
<grid-item tall>09</grid-item>
<grid-item short>10</grid-item>
<grid-item tallest>etc.</grid-item>
<grid-item tall></grid-item>
<grid-item taller></grid-item>
<grid-item short></grid-item>
<grid-item short></grid-item>
<grid-item short></grid-item>
<grid-item short></grid-item>
<grid-item tall></grid-item>
<grid-item short></grid-item>
<grid-item taller></grid-item>
<grid-item short></grid-item>
<grid-item tall></grid-item>
<grid-item short></grid-item>
<grid-item tall></grid-item>
<grid-item short></grid-item>
<grid-item short></grid-item>
<grid-item tallest></grid-item>
<grid-item taller></grid-item>
<grid-item short></grid-item>
<grid-item tallest></grid-item>
<grid-item tall></grid-item>
<grid-item short></grid-item>
</grid-container>
jsFiddle 演示
运作方式
建立块级网格容器。(将是另一种选择)
inline-grid
grid-auto-rows
属性设置自动生成的行的高度。在此网格中,每行的高度为 50px。grid-gap
属性是 和 的简写。此规则在网格项之间设置 10px 的间距。(它不适用于项目和容器之间的区域。grid-column-gap
grid-row-gap
grid-template-columns
属性设置显式定义列的宽度。重复表示法定义了
重复
列(或行)的模式。自动填充
功能告诉网格在不溢出容器的情况下排列尽可能多的列(或行)。(这可以创建与 flex 布局类似的行为。flex-wrap: wrap
minmax(
) 函数为每列(或行)设置最小和最大大小范围。在上面的代码中,每列的宽度最小为容器的 30%,最大为任何可用空间。fr
单位表示网格容器中可用空间的一小部分。它与 flexbox 的属性相当。flex-grow
评论
这是最近发现的涉及 flexbox 的技术:https://tobiasahlin.com/blog/masonry-with-css/。
这篇文章对我来说很有意义,但我没有尝试使用它,所以我不知道除了迈克尔的回答中提到的之外,是否有任何警告。
下面是文章中的一个示例,利用该属性,结合 .order
:nth-child
堆栈代码段
.container {
display: flex;
flex-flow: column wrap;
align-content: space-between;
/* Your container needs a fixed height, and it
* needs to be taller than your tallest column. */
height: 960px;
/* Optional */
background-color: #f7f7f7;
border-radius: 3px;
padding: 20px;
width: 60%;
margin: 40px auto;
counter-reset: items;
}
.item {
width: 24%;
/* Optional */
position: relative;
margin-bottom: 2%;
border-radius: 3px;
background-color: #a1cbfa;
border: 1px solid #4290e2;
box-shadow: 0 2px 2px rgba(0,90,250,0.05),
0 4px 4px rgba(0,90,250,0.05),
0 8px 8px rgba(0,90,250,0.05),
0 16px 16px rgba(0,90,250,0.05);
color: #fff;
padding: 15px;
box-sizing: border-box;
}
/* Just to print out numbers */
div.item::before {
counter-increment: items;
content: counter(items);
}
/* Re-order items into 3 rows */
.item:nth-of-type(4n+1) { order: 1; }
.item:nth-of-type(4n+2) { order: 2; }
.item:nth-of-type(4n+3) { order: 3; }
.item:nth-of-type(4n) { order: 4; }
/* Force new columns */
.break {
flex-basis: 100%;
width: 0;
border: 1px solid #ddd;
margin: 0;
content: "";
padding: 0;
}
body { font-family: sans-serif; }
h3 { text-align: center; }
<div class="container">
<div class="item" style="height: 140px"></div>
<div class="item" style="height: 190px"></div>
<div class="item" style="height: 170px"></div>
<div class="item" style="height: 120px"></div>
<div class="item" style="height: 160px"></div>
<div class="item" style="height: 180px"></div>
<div class="item" style="height: 140px"></div>
<div class="item" style="height: 150px"></div>
<div class="item" style="height: 170px"></div>
<div class="item" style="height: 170px"></div>
<div class="item" style="height: 140px"></div>
<div class="item" style="height: 190px"></div>
<div class="item" style="height: 170px"></div>
<div class="item" style="height: 120px"></div>
<div class="item" style="height: 160px"></div>
<div class="item" style="height: 180px"></div>
<div class="item" style="height: 140px"></div>
<div class="item" style="height: 150px"></div>
<div class="item" style="height: 170px"></div>
<div class="item" style="height: 170px"></div>
<span class="item break"></span>
<span class="item break"></span>
<span class="item break"></span>
</div>
评论
order
flex-flow: column wrap
order
最后,一个仅CSS的解决方案,可以轻松创建砌体布局,但我们需要耐心等待,因为目前不支持它。
此功能是在 CSS 网格布局模块级别 3 中引入的
本模块介绍了砌体布局作为 CSS 网格容器的附加布局模式。
然后
网格容器通过为其其中一个轴指定值 masonry 来支持砌体布局。该轴称为砌体轴,另一轴称为网格轴。
一个基本的例子是:
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-template-rows: masonry; /* this will do the magic */
grid-gap: 10px;
}
img {
width: 100%;
}
<div class="container">
<img src="https://picsum.photos/id/1/200/300">
<img src="https://picsum.photos/id/17/200/400">
<img src="https://picsum.photos/id/18/200/100">
<img src="https://picsum.photos/id/107/200/200">
<img src="https://picsum.photos/id/1069/200/600">
<img src="https://picsum.photos/id/12/200/200">
<img src="https://picsum.photos/id/130/200/100">
<img src="https://picsum.photos/id/203/200/100">
<img src="https://picsum.photos/id/109/200/200">
<img src="https://picsum.photos/id/11/200/100">
</div>
如果您启用该功能,这将在Firefox上产生以下结果,如下所示: https://caniuse.com/?search=masonry
- 打开 Firefox 并在 url 栏中写下 about:config
- 使用砌体进行搜索
- 你会得到一面旗帜,让它成为现实
如果我们减少屏幕屏幕,响应部分是完美的!
评论
我发现这个解决方案,它很可能与所有浏览器兼容。
注意:如果有人发现任何错误或浏览器支持问题。请更新此答案或评论
CSS Profpery 支持参考:
注意:此属性已替换为该属性。page-break-inside
break-inside
.container {
-moz-column-count: 1;
column-count: 1;
-moz-column-gap: 20px;
column-gap: 20px;
-moz-column-fill: balance;
column-fill: balance;
margin: 20px auto 0;
padding: 2rem;
}
.container .item {
display: inline-block;
margin: 0 0 20px;
page-break-inside: avoid;
-moz-column-break-inside: avoid;
break-inside: avoid;
width: 100%;
}
.container .item img {
width: 100%;
height: auto;
}
@media (min-width: 600px) {
.container {
-moz-column-count: 2;
column-count: 2;
}
}
@media (min-width: 900px) {
.container {
-moz-column-count: 3;
column-count: 3;
}
}
@media (min-width: 1200px) {
.container {
-moz-column-count: 4;
column-count: 4;
}
}
CSS-Only Masonry Layout
<div class="container">
<div class="item"><img src="https://placeimg.com/600/400/animals" alt=""></div>
<div class="item"><img src="https://placeimg.com/600/600/arch" alt=""></div>
<div class="item"><img src="https://placeimg.com/600/300/nature" alt=""></div>
<div class="item"><img src="https://placeimg.com/600/450/people" alt=""></div>
<div class="item"><img src="https://placeimg.com/600/350/tech" alt=""></div>
<div class="item"><img src="https://placeimg.com/600/800/animals/grayscale" alt=""></div>
<div class="item"><img src="https://placeimg.com/600/650/arch/sepia" alt=""></div>
<div class="item"><img src="https://placeimg.com/600/300/nature/grayscale" alt=""></div>
<div class="item"><img src="https://placeimg.com/600/400/people/sepia" alt=""></div>
<div class="item"><img src="https://placeimg.com/600/600/tech/grayscale" alt=""></div>
<div class="item"><img src="https://placeimg.com/600/200/animals/sepia" alt=""></div>
<div class="item"><img src="https://placeimg.com/600/700/arch/grayscale" alt=""></div>
</div>
评论
这是使用 CSS 的响应方式。
注意:元素是按列排序的,而不是按行排序的。columns
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<title>Responsive Grid Layout</title>
</head>
<body>
<div class="wrapper">
<div class="box box1">Content of box 1</div>
<div class="box box2">Content of box 2</div>
<div class="box box3">Content of box 3</div>
<div class="box box4">Content of box 4</div>
<div class="box box5">Content of box 5</div>
<div class="box box6">Content of box 6</div>
<div class="box box7">Content of box 7</div>
</div>
</body>
</html>
body {
margin: 0;
display: flex;
justify-content: center;
}
.wrapper {
column-count: auto;
column-width: 320px; /* 300px + 20px gap */
column-gap: 20px;
width: 100%;
max-width: 1000px;
padding: 20px;
box-sizing: border-box;
}
.box {
background-color: lightblue;
border: 1px solid blue;
padding: 20px;
font-size: 1.5em;
text-align: center;
width: 300px;
box-sizing: border-box;
display: inline-block;
margin-bottom: 20px;
break-inside: avoid-column;
}
.box1 {
height: 800px;
}
.box2 {
height: 300px;
}
.box3 {
height: 300px;
}
.box4 {
height: 300px;
}
评论