大きさの異なる画像がギャラリーのように並んでいて、それぞれの画像がぴったり敷き詰められているレイアウトが実現したくてやり方を調べた。 FlickrのExploreページのようなイメージ。
昔はこういったレイアウトの実現は複雑なことをしないといけなかった気がするが、最近は少しのCSSだけでいい感じのレイアウトが組めるようだ。
前提としては、次のCodepenの通り div#container の中にたくさんの img タグが並んでいて、それぞれの img の大きさが微妙に異なっているという状況。レイアウトを見えやすくするためにランダムな色もつけてある。
See the Pen Untitled by castaneai (@castaneai) on CodePen.
FlexBoxでいい感じにする
こういった多くのアイテムを並べる手法としてFlexBox というやり方がある。
アイテムを含む親(コンテナ)に対して display: flex
を指定すればよい。
#container { display: flex; }
containerの中にあるimgはFlex Item として扱われ、横並びになった。
コンテナの端で折り返す
コンテナの右端を超えたアイテムがはみ出して隠れてしまっているので、次の行に折り返すようにする。
#container { display: flex; flex-wrap: wrap; }
右端までアイテムを詰める
折り返すようになったが、その行にアイテムが入り切らないときに右端に余白が出て不格好なので、いい感じに詰めてほしい。
コンテナの横幅に合わせてアイテムの大きさが伸び縮みさせて、右端まできっちり詰めるようにする。アイテム側のCSSに flex-grow
を付けて適用する。
img { border: 1px solid red; flex-grow: 1; }
画像の縦横比を維持する
Flex Itemになると自動的に行ごとの高さが揃えられてきれいだが、その分画像が伸び縮みして縦長・横長に変形されているので、画像の縦横比を維持したまま伸び縮みさせる。 object-fit: cover
を使う。
img { border: 1px solid red; flex-grow: 1; object-fit: cover; }
(文字だけなので少し分かりづらいが)縦横比が維持された。ただし、画像の端が少し隠れてしまう場合もあるので注意。
行の高さを統一する
詰めるようにすると、アイテムの少ない行は極端に高さが大きすぎるので、高さは固定にしておく。
img { border: 1px solid red; flex-grow: 1; object-fit: cover; height: 200px; }
あとは仕上げとしてborderを消して、アイテムごとの余白をとって少し角丸にしたりすると…
img { flex-grow: 1; object-fit: cover; height: 200px; max-width: 400px; margin: 0.2rem; border-radius: 4px; }
いい感じになった! 少しのCSSでここまでできるのはすごい。
高さ固定ではなく、幅固定で並べる方法
今回は行の高さ固定で詰めて並べたが、Pinterestのように列の幅固定で高さの差を考慮して詰めるレイアウトは Masonry と呼ばれている。これは現状CSSだけでいい感じに作るのは難しそうだ1。
と思ったが、最新のCSSでは grid: masonry;
というものが提案されていてMasonryレイアウトもお手軽に実現できるようになりそう!
🔥 this is cool! Firefox is working on native CSS masonry layouts. Here's the CSS to create this screenshot:
— Josh W. Comeau (@JoshWComeau) October 22, 2020
.grid {
display: grid;
grid-gap: 1em;
grid: masonry / repeat(auto-fit, minmax(20em, 1fr));
}
The numbers represent DOM order. It stacks items like this 😮 pic.twitter.com/X1pzsaDSoS