1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

CSS Gridでオシャレタイル型レイアウト🐈

Posted at

祝!IEの呪縛から解き放たれて使えるCSS
といえばGrid、それとobject-fitも
その辺を使ったお手軽でフレキシブルで凝った雰囲気になるレイアウトを紹介します

作ったもの

ランダムなサイズの画像を、ある程度元の画像の比率を反映して良い感じに並べてみた。
画像はunsplushというフォトギャラリーAPIを使って持ってきています。

スクリーンショット 2023-11-15 134951.png

↓スクロールした動画あります
https://x.com/herishiro/status/1724651582065909835

こういうのを作るのに必要なコード

  • JavaScript : 8行
  • CSS property : 12行

今回はNext.js使ってるけどJQueryでもVanila JSでも多分何でもできます。

CSS Gridとは?

今更の説明ですが…

  • 縦と横のラインを引いて画面上に行と列を作り、そこにHTML要素を配置出来るやつ
  • Flexと違い、レスポンシブで要素の位置を複雑に入れ替えるなど自由度の高いレイアウトが可能

CSS-Grid-Empty-Space.png

実際のコード

Next.js + Emotion
import { css } from "@emotion/react";
import Image from "next/image";
import { useEffect, useState } from "react";
import { PhotoRes } from "../../../pages/api/unsplash";

export const ImageTileScreen = () => {
	const style = styler();
	// APIから取得した画像データ
	const [photos, setPhotos] = useState<PhotoRes["results"]>([]);

	useEffect(() => {
		const fetchImages = async () => {
			const res = await fetch("/api/unsplash");
			const photoRes: PhotoRes = await res.json();
			setPhotos(photoRes.results);
		};
		fetchImages();
	}, []);

	const getGridSpan = (width: number, height: number) => {
		const ratio = width / height;
		const isLandscape = ratio > 1;
		const isRect = ratio > 1.3;
		const column = isLandscape ? Math.ceil(ratio) : 1;
		const row = isLandscape ? (isRect ? 2 : 1) : Math.ceil(1.8 / ratio);

		return { gridColumn: `span ${column}`, gridRow: `span ${row}` };
	};

	return (
		<div css={style.container}>
			{photos.map((photo) => {
				return (
					<div
						key={photo.urls.regular}
						css={[style.card, getGridSpan(photo.width, photo.height)]}
					>
						<Image
							src={photo.urls.regular}
							alt={photo.alt_description}
							width={photo.width}
							height={photo.height}
						/>
					</div>
				);
			})}
		</div>
	);
};

const styler = () => {
	return {
		container: css({
			position: "relative",
			display: "grid",
			gap: 10,
			gridTemplateColumns: `repeat(3, 1fr)`,
			gridAutoRows: "1fr",
			gridAutoFlow: "dense",
			padding: "0 5%",
		}),
		card: css({
			position: "relative",
			borderRadius: 10,
			overflow: "hidden",
			span: {
				height: "100% !important",
				img: {
					objectFit: "cover",
				},
			},
		}),
	};
};

仕組み

  1. 画面全体にGrid layoutで3列のgridを引く
  2. 各画像の縦横サイズを比較して、横長の画像なら1行×縦比率分のn列、縦長の場合は横比率分のn行×1列のグリッドを割り当てるcssを追加する。
    (一定以上正方形に近い場合は2行×2列にする)
  3. 隙間を上手いこと埋めるのはCSSが勝手にやってくれる!

素敵な新入りのプロパティたち

gap: <マージンサイズ>

コンテナ要素に指定して要素”間”のマージンを指定出来る。

従来のmargin-rightでは必要だった最後の要素には付けないなどの工夫が不要になる!

なお2列の要素の縦の間隔にも使える。flexでも使える。

IGlZi.png

grid-template-columns: repeat(<繰り返し回数>, <列の幅サイズ>)

コンテナ要素に指定して、repeat()内の第1引数に列の数、第2引数に各列のサイズを渡すことで等間隔のgridの列を引くことが出来る。

今回第二引数に渡している”1fr”のfrは、固定で指定した幅やマージンを除いた部分をよしなに埋めてくれるgridと一緒に登場した便利な単位。

gridTemplateColumns: `repeat(3, 1fr)`,

よってこの指定の意味は以下になる

画面幅に関わらず、gapやpadding, marginを除いた幅を3等分した位置で画面を分割する。

grid-auto-flow: dense

要素ごとのサイズにばらつきがある時に、なるべくコンテナ内に綺麗に並ぶようにしてくれる。

ご覧のように限界はあるが、なるべく左上に向けて要素を詰めて表示されている。

※要素の順番はバラバラになるので注意!

スクリーンショット 2022-09-02 232512.png

object-fit: cover;

object-fitを画像を囲んだ要素に指定することで、画像のサイズや縦横比がどこを基準にするか決められる

coverは縦横比を保ちつつ、要素内に余白が出ないようにサイズを調整する(=縦と横で幅が広い方の長さに合わせて、要素に入りきらない部分は枠外にはみ出す)

今回は外部APIから取得した画像なので使えなかったけど、この辺の操作はbackground-imageで指定して、その関連プロパティを使った方が扱いやすい気もする

Untitled.png

余談

gridを20列にして、細かく縦横比を指定したらもっとカッコ良くなるかと思ったら、grid-auto-flow: denseが上手くかみ合わなくてむしろ見た目が悪くなってしまった><

スクリーンショット 2022-09-02 234541.png

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?