CSS: 大きさの異なる画像をいい感じに敷き詰めて並べる

大きさの異なる画像がギャラリーのように並んでいて、それぞれの画像がぴったり敷き詰められているレイアウトが実現したくてやり方を調べた。 FlickrのExploreページのようなイメージ。

f:id:castaneai:20220211150738j:plain
https://www.flickr.com/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 として扱われ、横並びになった。

f:id:castaneai:20220211142738p:plain

コンテナの端で折り返す

コンテナの右端を超えたアイテムがはみ出して隠れてしまっているので、次の行に折り返すようにする。

#container {
  display: flex;
  flex-wrap: wrap;
}

f:id:castaneai:20220211142841p:plain

右端までアイテムを詰める

折り返すようになったが、その行にアイテムが入り切らないときに右端に余白が出て不格好なので、いい感じに詰めてほしい。

コンテナの横幅に合わせてアイテムの大きさが伸び縮みさせて、右端まできっちり詰めるようにする。アイテム側のCSSに flex-grow を付けて適用する。

img {
  border: 1px solid red;
  flex-grow: 1;
}

f:id:castaneai:20220211142956p:plain

画像の縦横比を維持する

Flex Itemになると自動的に行ごとの高さが揃えられてきれいだが、その分画像が伸び縮みして縦長・横長に変形されているので、画像の縦横比を維持したまま伸び縮みさせる。 object-fit: cover を使う。

img {
  border: 1px solid red;
  flex-grow: 1;
  object-fit: cover;
}

(文字だけなので少し分かりづらいが)縦横比が維持された。ただし、画像の端が少し隠れてしまう場合もあるので注意。

f:id:castaneai:20220211143427p:plain

行の高さを統一する

詰めるようにすると、アイテムの少ない行は極端に高さが大きすぎるので、高さは固定にしておく。

img {
  border: 1px solid red;
  flex-grow: 1;
  object-fit: cover;
  height: 200px;
}

f:id:castaneai:20220211143955p:plain

あとは仕上げとしてborderを消して、アイテムごとの余白をとって少し角丸にしたりすると…

img {
  flex-grow: 1;
  object-fit: cover;
  height: 200px;
  max-width: 400px;
  margin: 0.2rem;
  border-radius: 4px;
}

f:id:castaneai:20220211145808p:plain

いい感じになった! 少しのCSSでここまでできるのはすごい。

高さ固定ではなく、幅固定で並べる方法

今回は行の高さ固定で詰めて並べたが、Pinterestのように列の幅固定で高さの差を考慮して詰めるレイアウトは Masonry と呼ばれている。これは現状CSSだけでいい感じに作るのは難しそうだ1

f:id:castaneai:20220211145153p:plain
https://w3bits.com/css-masonry/ より引用

と思ったが、最新のCSSでは grid: masonry; というものが提案されていてMasonryレイアウトもお手軽に実現できるようになりそう!