web-dev-qa-db-ja.com

CSSのみの石積みレイアウト

かなりすぐに使える石積みのレイアウトを実装する必要があります。ただし、いくつかの理由から、JavaScriptを使用してそれを行いたくありません。

A grid of multiple columns of rectangles of varying height.

パラメーター:

  • すべての要素の幅は同じです
  • 要素の高さはサーバー側で計算できません(画像とさまざまな量のテキスト)
  • 必要に応じて固定数の列で生活できます

これには、最新のブラウザで機能する簡単な解決策があります column-countプロパティ。

その解決策の問題は、要素が列で順序付けられていることです:

Starting from the top leftmost box, they're numbered 1 through 4 straight down, the topmost box in the next column is 5, and so on.

要素を行単位で並べる必要がありますが、少なくともおよそ:

Starting from the top leftmost box, they're numbered 1 through 6 straight across, but because box 5 is the shortest the box underneath it is 7 as it has the appearance of being on a row higher than the next box on the far left.

私が試したアプローチはうまくいきません:

今私はcouldサーバー側のレンダリングを変更し、アイテムの数を列の数で割ってアイテムを並べ替えますが、それは複雑で、エラーが発生しやすい(ベースブラウザがアイテムリストを列に分割する方法について)、可能な限り避けたいと思います。

これを可能にするいくつかの新しいflexboxマジックがありますか?

92
Pekka 웃

Flexbox

動的な石積みレイアウトは、少なくともクリーンで効率的な方法ではなく、flexboxでは不可能です。

Flexboxは1次元のレイアウトシステムです。これは、水平OR垂直線に沿ってアイテムを整列できることを意味します。フレックスアイテムは、その行または列に限定されます。

真のグリッドシステムは2次元です。つまり、水平線と垂直線に沿ってアイテムを整列できます。コンテンツアイテムは行と列に同時にまたがることができますが、フレックスアイテムではできません。

これが、flexboxがグリッドを構築する能力に制限がある理由です。また、W3Cが別のCSS3テクノロジー Grid Layout を開発した理由でもあります。


row wrap

flex-flow: row wrapを持つflexコンテナでは、flexアイテムは新しいrowsにラップする必要があります。

これは、同じ行の別のアイテムの下にフレックスアイテムをラップできないことを意味します

div#3div#1の下でどのように折り返され、新しい行が作成されるかに注意してください。div#2の下にラップすることはできません。

その結果、アイテムが行の中で最も高くない場合、空白が残り、見苦しいギャップが生じます。


column wrap

flex-flow: column wrapに切り替えると、グリッドのようなレイアウトがより実現可能になります。ただし、列方向のコンテナーには、すぐに4つの潜在的な問題があります。

  1. Flexアイテムは、水平ではなく垂直に流れます(この場合に必要です)。
  2. コンテナは、Pinterestレイアウトのように、垂直方向ではなく水平方向に拡大します。
  3. コンテナの高さを固定する必要があるため、アイテムはラップする場所を認識します。
  4. この記事の執筆時点では、 コンテナが追加の列 を収容するために拡張しないすべての主要なブラウザに欠陥があります。

その結果、この場合、および他の多くの場合、列方向コンテナはオプションではありません。


CSSグリッドアイテムの寸法が未定義

グリッドレイアウトは、コンテンツアイテムのさまざまな高さを事前に決定できる場合は、問題に対する完璧なソリューションになります。他のすべての要件は、グリッドの能力の範囲内です。

周囲のアイテムとのギャップを埋めるために、グリッドアイテムの幅と高さがわかっている必要があります。

そのため、水平に流れる石積みレイアウトを構築するためにCSSが提供しなければならないグリッドは、この場合には不十分です。

実際、CSSテクノロジーが自動的にギャップを埋める機能を備えて登場するまで、CSSには一般に解決策がありません。このようなものはおそらくドキュメントをリフローする必要があるので、それがどれほど有用か効率的かはわかりません。

スクリプトが必要になります。

JavaScriptソリューションは、絶対配置を使用する傾向があります。これは、ギャップなしでコンテンツアイテムを再配置するために、ドキュメントフローからコンテンツアイテムを削除します。以下に2つの例を示します。


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 demo


仕組み

  1. ブロックレベルのグリッドコンテナを確立します。inline-gridは他のオプションです)
  2. grid-auto-rows プロパティは、自動生成された行の高さを設定します。このグリッドでは、各行の高さは50ピクセルです。
  3. grid-gap プロパティは、grid-column-gapおよびgrid-row-gapの省略形です。このルールは、10pxギャップbetweenグリッドアイテムを設定します。 (アイテムとコンテナの間の領域には適用されません。)
  4. grid-template-columns プロパティは、明示的に定義された列の幅を設定します。

    repeat 表記は、繰り返し列(または行)のパターンを定義します。

    auto-fill 関数は、可能な限り多くの列(または行)をオーバーフローさせずに整列するようにグリッドに指示します容器。 (これにより、flexレイアウトのflex-wrap: wrapと同様の動作を作成できます。)

    minmax() 関数は、各列(または行)の最小および最大サイズ範囲を設定します。上記のコードでは、各列の幅はコンテナーの最小30%で、使用可能な空きスペースの最大になります。

    frunit は、グリッドコンテナの空き領域の一部を表します。 flexboxのflex-growプロパティに匹敵します。

  5. grid-row および span グリッドアイテムにまたがる行数を伝えています。


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/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts

128
Michael_B

これは最近発見されたフレックスボックスを含むテクニックです: 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>
3