簡體   English   中英

如何使用 CSS 創建基於行的砌體布局?

[英]How can I create a row-based masonry layout using CSS?

我需要實施砌體布局。 但是,出於多種原因,我不想使用 JavaScript 來完成它。

由多列不同高度的矩形組成的網格。

參數:

  • 所有元素具有相同的寬度
  • 元素具有服務器端無法計算的高度(圖像加上不同數量的文本)
  • 如果必須的話,我可以接受固定數量的列

有一個適用於現代瀏覽器的簡單解決方案,column-count屬性。

該解決方案的問題在於元素按列排序:

從最左上方的方框開始,它們的編號從 1 到 4 一直向下,下一列中最上方的方框是 5,依此類推。

雖然我需要按行對元素進行排序,但至少大致如下:

從最左上角的盒子開始,它們的編號為 1 到 6,但由於盒子 5 是最短的,所以它下面的盒子是 7,因為它看起來比最左邊的下一個盒子高一排。

我嘗試過但不起作用的方法:

現在我可以更改服務器端呈現並重新排序項目,將項目數除以列數,但這很復雜,容易出錯(基於瀏覽器決定將項目列表拆分為列的方式),所以我想盡可能避免它。

是否有一些 flexbox 魔法使這成為可能?

2021 Update

CSS Grid Layout Level 3 includes a masonry feature.

Code will look like this:

grid-template-rows: masonry
grid-template-columns: masonry

As of March 2021, it's only available in Firefox (after activating the flag).

end update; original answer below


Flexbox

A dynamic masonry layout is not possible with flexbox, at least not in a clean and efficient way.

Flexbox is a one-dimensional layout system. This means it can align items along horizontal OR vertical lines. A flex item is confined to its row or column.

A true grid system is two-dimensional, meaning it can align items along horizontal AND vertical lines. Content items can span across rows and columns simultaneously, which flex items cannot do.

This is why flexbox has a limited capacity for building grids. It's also a reason why the W3C has developed another CSS3 technology, Grid Layout.


row wrap

In a flex container with flex-flow: row wrap, flex items must wrap to new rows.

This means that a flex item cannot wrap under another item in the same row.

Notice above how div #3 wraps below div #1, creating a new row. It cannot wrap beneath div #2.

As a result, when items aren't the tallest in the row, white space remains, creating unsightly gaps.


column wrap

If you switch to flex-flow: column wrap, a grid-like layout is more attainable. However, a column-direction container has four potential problems right off the bat:

  1. Flex items flow vertically, not horizontally (like you need in this case).
  2. The container expands horizontally, not vertically (like the Pinterest layout).
  3. It requires the container to have a fixed height, so the items know where to wrap.
  4. As of this writing, it has a deficiency in all major browsers where the container doesn't expand to accommodate additional columns.

As a result, a column-direction container is not an option in this case, and in many other cases.


CSS Grid with item dimensions undefined

Grid Layout would be a perfect solution to your problem if the various heights of the content items could be pre-determined. All other requirements are well within Grid's capacity.

The width and height of grid items must be known in order to close gaps with surrounding items.

So Grid, which is the best CSS has to offer for building a horizontally-flowing masonry layout, falls short in this case.

In fact, until a CSS technology arrives with the ability to automatically close the gaps, CSS in general has no solution. Something like this would probably require reflowing the document, so I'm not sure how useful or efficient it would be.

You'll need a script.

JavaScript solutions tend to use absolute positioning, which removes content items from the document flow in order to re-arrange them with no gaps. Here are two examples:

Masonry is a JavaScript grid layout library. It works by placing elements in optimal position based on available vertical space, sort of like a mason fitting stones in a wall.

source: http://masonry.desandro.com/

[Pinterest] really is a cool site, but what I find interesting is how these pinboards are laid out... So the purpose of this tutorial is to re-create this responsive block effect ourselves...

source: https://benholland.me/javascript/2012/02/20/how-to-build-a-site-that-works-like-pinterest.html


CSS Grid with item dimensions defined

For layouts where the width and height of content items are known, here's a horizontally-flowing masonry layout in pure 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 demo


How it works

  1. Establish a block-level grid container. (inline-grid would be the other option)
  2. The grid-auto-rows property sets the height of automatically generated rows. In this grid each row is 50px tall.
  3. The grid-gap property is a shorthand for grid-column-gap and grid-row-gap. This rule sets a 10px gap between grid items. (It doesn't apply to the area between items and the container.)
  4. The grid-template-columns property sets the width of explicitly defined columns.

The repeat notation defines a pattern of repeating columns (or rows).

The auto-fill function tells the grid to line up as many columns (or rows) as possible without overflowing the container. (This can create a similar behavior to flex layout's flex-wrap: wrap.)

The minmax() function sets a minimum and maximum size range for each column (or row). In the code above, the width of each column will be a minimum of 30% of the container and maximum of whatever free space is available.

The fr unit represents a fraction of the free space in the grid container. It's comparable to flexbox's flex-grow property.

  1. With grid-row and span we're telling grid items how many rows they should span across.

Browser Support for CSS Grid

  • Chrome - full support as of March 8, 2017 (version 57)
  • Firefox - full support as of March 6, 2017 (version 52)
  • Safari - full support as of March 26, 2017 (version 10.1)
  • Edge - full support as of October 16, 2017 (version 16)
  • IE11 - no support for current spec; supports obsolete version

Here's the complete picture: http://caniuse.com/#search=grid


Cool grid overlay feature in Firefox

In Firefox dev tools, when you inspect the grid container, there is a tiny grid icon in the CSS declaration. On click it displays an outline of your grid on the page.

More details here: https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts

這是最近發現的涉及 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>

最后是一個純 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

  1. 打開 Firefox 並在 url 欄中寫入 about:config
  2. 使用磚石進行搜索
  3. 你會得到一個標志,讓它成為現實

CSS砌體布局

如果我們把屏幕屏幕縮小,響應式部分就完美了

僅使用 CSS 的砌體布局

 .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>

試試這些簡單的 2 行 CSS

.parent-container{
    column-count: 3;
    column-gap: 30px;
}
.child-elements{
    display: inline-block;
    width: 100%;
}

而已! 我希望這可以幫助你~RDaksh

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM