使用CSS网格布局实现砌体布局

15
我正在尝试使用CSS网格布局创建砌体布局。网格中的所有项目都具有可变高度,并且我不知道将使用哪些项目。因此,我无法为每个项目定义grid-row。是否可以在上一个项目结束后立即在列中启动每个新项目?
我正在尝试的代码:
.wrapper {
  display: grid;
  grid-template-columns: repeat(auto-fill, 330px);
  align-items: flex-start;
  grid-column-gap: 10px;
  grid-row-gap: 50px;
}

.item {
  background: black;
  border-radius: 5px;
}
<div class="wrapper">
  <div class="item" style="height:50px"></div>
  <div class="item" style="height:100px"></div>
  <div class="item" style="height:30px"></div>
  <div class="item" style="height:90px"></div>
  <div class="item" style="height:80px"></div>
  <div class="item" style="height:50px"></div>
  <div class="item" style="height:70px"></div>
  <div class="item" style="height:40px"></div>

</div>

点击这里查看完整的CodePen页面


1
据我所知,CSS Grid 对瀑布流布局并没有比其他布局方法更好的支持。 - Paulie_D
3
如果您可以在网格中定义高度,CSS Grid 可以很好地处理砖石排列。虽然它可能不是完全自动化的,但在许多情况下仍然很有用。https://dev59.com/P1cP5IYBdhLWcg3wvMhF#43903119 - Michael Benjamin
@user2950602,如果你正在寻找其他选择,请查看我在这里的答案:https://dev59.com/QFsW5IYBdhLWcg3w4qiw - Michael Benjamin
@Michael_B 问题是我无法定义高度。感谢提供其他选项的链接。 - user2950602
如果您可以将隐式行设置为5px,那将是很酷的。 网格项可以跨越尽可能多的行。 在当前布局中,这意味着垂直白色空间消失了,但它也使网格项重叠。 但是,如果网格具有指示每个项目“查找未占用单元格”的属性,则可以解决重叠问题,垂直间隙将消失,并且可以使用grid-gapmargin来控制分隔。 遗憾的是,我不知道规范是否提供此功能,但如果有的话,那将是很棒的。 - Michael Benjamin
显示剩余2条评论
4个回答

2

在你的问题中,你正在单独设置每个项目的高度。如果你愿意这样做,那么可以使用网格轻松实现砌体布局。

不要为每个项目设置高度,而是设置grid-row-end,使每个项目跨越一定数量的行。

 <div class="item" style="grid-row-end: span 5"></div>

该项的高度将取决于您为网格设置的 grid-auto-rowsgrid-row-gap 值。这里有一个 Codepen 的示例:https://codepen.io/andybarefoot/pen/NaprOB。如果您不想为每个项目单独设置grid-row-end值,可以使用一些 JavaScript 动态设置它。我在每个项目中添加了另一个“容器”div,并测量此容器的高度,以计算该项目需要跨越多少行。我在页面加载时进行此操作,并在任何图像加载时再次进行此操作(因为内容的高度将发生更改)。如果您将此方法与响应式布局相结合,则还应在页面调整大小时重新计算,因为列的宽度可能已更改,从而影响内容的高度。这里是具有响应式列调整的完整示例:https://codepen.io/andybarefoot/pen/QMeZda。如果您有宽度可变的项目,仍然可以实现类似的效果,但是网格的排列可能不会完美,项目顺序可能会更改以优化网格的填充。我在 Medium 上写了一篇关于此方法的博客,以防感兴趣:使用 CSS Grid 的 Masonry 样式布局。

1
你可以动态设置 grid-row-endspan 值(例如下面示例中基于我的 Codepen 实验 的 JS 代码),并在 grid-auto-placement 中使用 dense 关键字。

const gridStyles = getComputedStyle(document.querySelector('.wrapper',null));
const rowHeight = parseInt(gridStyles.getPropertyValue('--grid-row-height'));
const gap = parseInt(gridStyles.getPropertyValue('--grid-gutter'));;

let makeGrid = function() {
  let items = document.querySelectorAll('.item');
  for (let i=0, item; item = items[i]; i++) {
    // take an item away from grid to measure it
    item.classList.add('is-being-measured');
    let height = item.offsetHeight;
    // calcylate the row span
    let rowSpan = Math.ceil((height + gap)/(rowHeight + gap));
    // set the span value for grid-row-end
    item.style.gridRowEnd = 'span '+rowSpan;
    // return the item into the grid
    item.classList.remove('is-being-measured');
  }
}

window.addEventListener('load', makeGrid);
window.addEventListener('resize', () => {
  clearTimeout(makeGrid.resizeTimer);
  makeGrid.resizeTimer = setTimeout(makeGrid, 50);
});
.wrapper {
  display: grid;
  grid-template-columns: repeat(auto-fill, 330px);
  --grid-gutter: 10px;
  grid-gap: var(--grid-gutter);
  --grid-row-height: 10px;
  grid-auto-rows: var(--grid-row-height);
  grid-auto-flow: row dense;
  position: relative;
}

.item {
  background: black;
  color: white;
  border-radius: 5px;
}
.item.is-being-measured {
  /* temporary styles for measuring grid items */
  position: absolute;
  width: 330px;
  top: 0;
  left: 0;
}

.item > * { margin-left: 20px; }
<div class="wrapper">
  <div class="item"><h3>1.1</h3><p>1.2</p></div>
  <div class="item"><p>2.1</p><p>2.2</p><p>2.3</p><p>2.4</p><p>2.5</p></div>
  <div class="item"><h2>3.1</h2></div>
  <div class="item"><h2>4.1</h2><p>4.2</p><p>4.3</p><p>4.4</p></div>
  <div class="item"><p>5.1</p><p>5.2</p><p>5.3</p><p>5.4</p></div>
  <div class="item"><h2>6.1</h2><p>6.2</p></div>
  <div class="item"><h2>7.1</h2><p>7.2</p><p>7.3</p></div>
  <div class="item"><p>8.1</p><p>8.2</p></div>

</div>


0

这是一种只使用CSS创建砌体布局的方法。

*,
*:before,
*:after {
  box-sizing: border-box !important;
}

article {
  -moz-column-width: 13em;
  -webkit-column-width: 13em;
  -moz-column-gap: 1em;
  -webkit-column-gap: 1em;
}

section {
  display: inline-block;
  margin: 0.25rem;
  padding: 1rem;
  width: 100%;
  background: #efefef;
}

p {
  margin: 1rem 0;
}

body {
  line-height: 1.25;
}
<article>
  <section>
    <p>Lorem ipsum dolor sit amet, consectetur.</p>
  </section>
  <section>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aliquid reprehenderit expedita odio beatae est.</p>
  </section>
  <section>
    <p>Lorem ipsum dolor sit amet, consectetur.</p>
  </section>
  <section>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nobis quaerat suscipit ad.</p>
  </section>
  <section>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Rem nihil alias amet dolores fuga totam sequi a cupiditate ipsa voluptas id facilis nobis.</p>
  </section>
  <section>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Rem ut debitis dolorum earum expedita eveniet voluptatem quibusdam facere eos numquam commodi ad iusto laboriosam rerum aliquam.</p>
  </section>
  <section>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
  </section>
  <section>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quaerat architecto quis tenetur fugiat veniam iste molestiae fuga labore!</p>
  </section>
  <section>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Odit accusamus tempore at porro officia rerum est impedit ea ipsa tenetur. Labore libero hic error sunt laborum expedita.</p>
  </section>
  <section>
    <p>Lorem ipsum dolor sit.</p>
  </section>
  <section>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Minima asperiores eveniet vero velit eligendi aliquid in.</p>
  </section>
  <section>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Doloribus dolorem maxime minima animi cum.</p>
  </section>
</article>

注意:这段代码不是我写的,我只是在原有代码基础上做了一些小的调整,原始代码可以在这里找到。


请注意,正如Zen所指出的那样:

[...] 项目按照从上到下从左到右的方式布局,而通常期望的是(文化假设除外)从左到右从上到下的布局。这是通常基于CSS3列的建议的绊脚石。


8
这个解决方案的问题在于,它的项目是从上到下、从左到右排列的,而通常人们期望的是从左到右、从上到下的布局方式(忽略文化差异)。这是通常基于CSS3列的建议无法实现的阻碍。 - Zen

-2
你可以通过 column 来实现这个功能。
.wrapper {
    column-gap: 10px;
    column-count: 4;
}

.item {
    display: inline-block;
    background: #000;
    width: 100%;
    border-radius: 3px;
}

看起来你试图使用flexgrid的组合,这可能会让事情变得混乱。据我所知,flex相对于页面上其他项目是相对的,而设置列会影响落入这些列中的项目。

更新的CodePen


虽然这理论上回答了问题,但最好在此处包含答案的基本部分并提供参考链接。仅有链接的答案可能会因为链接页面的更改而失效。 - Paulie_D
@WillThresher,是什么让你认为“flex相对于页面上的其他项目”?Flexbox的特殊规则仅适用于具有display:flex属性的元素的直接后代。 - Ilya Streltsyn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接