仅CSS的砌体布局


134

我需要实现相当合理的砌体布局。但是,由于多种原因,我不想使用JavaScript来实现它。

高度可变的矩形的多列网格。

参数:

  • 所有元素具有相同的宽度
  • 元素具有服务器端无法计算的高度(图像加上各种文本)
  • 如果必须,我可以使用固定数量的列

有一个简单的解决方案,可以在现代浏览器中运行,column-count属性。

该解决方案的问题在于元素按列排序:

从最左上方的框开始,它们从1到4一直向下编号,下一列的最上方的框为5,依此类推。

虽然我需要按行对元素进行排序,但至少要大致:

从最左上角的框开始,它们的编号是从1到6的直线,但是因为框5是最短的框,所以它的下方是7,因为它看起来比最左边的下一个框高。

我尝试过的方法不起作用:

现在,我可以更改服务器端渲染并重新排序项目,将项目数除以列数,但这很复杂,容易出错(基于浏览器决定如何将项目列表拆分为列),所以我想尽可能避免它。

是否有一些新的flexbox魔术使这成为可能?


6
想不到一种不依赖于预定义高度的方法。如果您重新考虑JS,请看一下stackoverflow.com/questions/13518653/…,在这里我实现了一个非常简单的解决方案。
加布里埃莱·彼得里奥里

1
@Gaby立即实施您的解决方案,谢谢!
佩卡

4
我意识到您说的只是CSS。我只想提到Masonry不再需要jQuery-缩小的库小于8kb-可以单独使用html进行初始化。仅供参考jsfiddle.net/wp7kuk1t

1
如果您可以提前确定元素的高度,则通过了解行高,字体大小(必须提供特定字体并进行一些巧妙的计算),图像高度,垂直边距和填充,您可以做这个。否则,您不能仅使用CSS来执行此操作。您还可以使用PhantomJS之类的东西来预渲染每个元素并获取该元素的高度,但是会增加大量的开销/延迟。
蒂莫西·佐恩

Answers:


157

弹性盒

Flexbox无法实现动态砌体布局,至少不能以一种干净有效的方式。

Flexbox是一维布局系统。这意味着它可以沿水平或垂直线对齐项目。弹性项目仅限于其行或列。

真正的网格系统是二维的,这意味着它可以沿水平和垂直线对齐项目。内容项目可以同时跨越行和列,而弹性项目则不能。

这就是为什么flexbox构建网格的能力有限的原因。这也是W3C开发了另一种CSS3技术Grid Layout的原因


row wrap

在带有flex容器中flex-flow: row wrap,flex项必须包装到新

这意味着弹性项目不能包装在同一行中的另一个项目之下

请注意上面的div#3如何在div#1之下包装,从而创建新行。它不能环绕在div#2下

结果,当项目不是该行中最高的项目时,将保留空白,从而造成难看的间隙。


column wrap

如果切换到flex-flow: column wrap,则更可能获得网格状的布局。但是,列方向容器马上有四个潜在问题:

  1. Flex项目是垂直流动的,而不是水平流动的(如本例所示)。
  2. 容器水平扩展,而不是垂直扩展(如Pinterest布局)。
  3. 它要求容器具有固定的高度,因此物品知道在哪里包装。
  4. 在撰写本文时,它在所有主流浏览器中都有一个缺陷,在该主流浏览器中,容器不会扩展以容纳其他列

结果,在这种情况下以及在许多其他情况下,列方向容器不是一个选项。


项目尺寸未定义的 CSS网格

如果可以预先确定内容项的各种高度,则网格布局将是解决您的问题的理想解决方案。所有其他要求都在Grid的能力范围内。

必须知道网格项目的宽度和高度,以缩小与周围项目的间隙。

因此,在这种情况下,Grid是最好的CSS,它可以用来构建水平流动的砖石布局。

实际上,除非CSS技术能够自动缩小差距,否则CSS通常没有解决方案。这样的事情可能需要重排文档,所以我不确定它会多么有用或有效。

您将需要一个脚本。

JavaScript解决方案倾向于使用绝对定位,该绝对定位从文档流中删除内容项,以便无间隙地重新排列它们。这是两个示例:


定义了项目尺寸的 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演示


这个怎么运作

  1. 建立一个块级网格容器。inline-grid将是另一个选择)
  2. grid-auto-rows属性设置自动生成的行的高度。在此网格中,每行高度为50px。
  3. grid-gap属性是一个速记grid-column-gapgrid-row-gap。此规则网格项目之间设置10px的间距。(不适用于物品和容器之间的区域。)
  4. grid-template-columns属性设置显式定义的列的宽度。

    repeat符号定义重复列(或行)的模式。

    auto-fill函数告诉网格尽可能多地排列列(或行),而不会使容器溢出。(这可以创建与flex布局类似的行为flex-wrap: wrap。)

    minmax()函数为每列(或行)设置最小和最大大小范围。在上面的代码中,每列的宽度最小为容器的30%,最大为可用的可用空间。

    fr单元表示在网格容器中的自由空间的一小部分。相当于flexbox的flex-grow属性。

  5. 使用grid-rowspan告诉网格项目它们应该跨越多少行。


浏览器对CSS网格的支持

  • Chrome-自2017年3月8日起全面支持(版本57)
  • Firefox-截至2017年3月6日(版本52)全面支持
  • Safari-自2017年3月26日起提供全面支持(版本10.1)
  • Edge-截至2017年10月16日(版本16)全面支持
  • IE11-不支持当前规范;支持过时的版本

这是完整的图片:http : //caniuse.com/#search=grid


Firefox中的超酷网格叠加功能

在Firefox开发工具中,当您检查网格容器时,CSS声明中有一个微小的网格图标。单击时,它会在页面上显示网格的轮廓。

此处有更多详细信息:https : //developer.mozilla.org/zh-CN/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts


5
很棒的答案!使用“已定义项目尺寸的CSS网格”解决方案,是否可以将单元格的高度表示为单元格宽度的百分比?这对于显示纵横比已知的图像很有用。我们要一直保持长宽比。
奥利弗·约瑟夫·阿什

谢谢。关于图像的宽高比问题,使用“单元格宽度百分比”方法(百分比填充技巧?),就此而言,在Grid或Flexbox中是不可靠的。看到这里这里。@OliverJosephAsh
Michael Benjamin

是的,我指的是百分比填充技巧。是否可以将任何变通办法与您的砌体布局解决方案结合使用?对于上下文,我们希望为unsplash.com上的图像实现仅CSS的砌体布局。
奥利弗·约瑟夫·阿什

1
不幸的是,为此无法使用JS。出于性能和SEO的原因,我们希望启用服务器端呈现。这意味着必须在客户端JavaScript下载,解析和执行之前呈现布局。鉴于这似乎是不可能的,我想我们必须沿路线进行权衡!感谢您的帮助:-)
奥利弗·约瑟夫·阿什

1
规格更新:在flexbox中,现在设置了边距和填充百分比以再次解析容器的内联大小。drafts.c​​sswg.org/css-flexbox/#item-margins @OliverJosephAsh
Michael Benjamin

13

最近发现了涉及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>


首先,仅链接的答案是错误的答案,例如当链接消失时,答案也是如此。其次,如果您更仔细地阅读给定的答案,则会发现链接中提到的内容。简而言之,单独使用Flexbox会有太多限制,除非上面提到的不是问题。
艾森(Ason)

3
我已经非常彻底地阅读了接受的答案-您甚至还会看到一些我在其底部添加的评论。我链接到的flexbox方法是唯一的,并且未包含在该答案中。我不想重复该文章,因为它非常复杂,并且本文对此做了出色的工作。
奥利弗·约瑟夫·阿什

链接所做的唯一不同的事情是利用该order属性,使该属性看起来像项目从左到右流动。尽管如此,它的主要技巧仍是flex-flow: column wrap,在以上答案中已提及。另外,它不能像砌体一样流动物品,例如,如果第3列和第4物品适合第3列,则链接的物品就不能。但是正如我所说,这完全取决于要求,在这种情况下(此问题),它不能按要求工作。
艾森(Ason)

此外,这与“您不想重复本文”无关,答案应包含代码的基本部分,因此当链接的资源消失时,它仍然有效。
艾森(Ason)

3
我对其进行了更新,并添加了文章+推荐中的示例。
艾森(Ason)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.