祝!IEの呪縛から解き放たれて使えるCSS
といえばGrid、それとobject-fitも
その辺を使ったお手軽でフレキシブルで凝った雰囲気になるレイアウトを紹介します
作ったもの
ランダムなサイズの画像を、ある程度元の画像の比率を反映して良い感じに並べてみた。
画像はunsplushというフォトギャラリーAPIを使って持ってきています。
↓スクロールした動画あります
https://x.com/herishiro/status/1724651582065909835
こういうのを作るのに必要なコード
- JavaScript : 8行
- CSS property : 12行
今回はNext.js使ってるけどJQueryでもVanila JSでも多分何でもできます。
CSS Gridとは?
今更の説明ですが…
- 縦と横のラインを引いて画面上に行と列を作り、そこにHTML要素を配置出来るやつ
- Flexと違い、レスポンシブで要素の位置を複雑に入れ替えるなど自由度の高いレイアウトが可能
実際のコード
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",
},
},
}),
};
};
仕組み
- 画面全体にGrid layoutで3列のgridを引く
- 各画像の縦横サイズを比較して、横長の画像なら1行×縦比率分のn列、縦長の場合は横比率分のn行×1列のグリッドを割り当てるcssを追加する。
(一定以上正方形に近い場合は2行×2列にする) - 隙間を上手いこと埋めるのはCSSが勝手にやってくれる!
素敵な新入りのプロパティたち
gap: <マージンサイズ>
コンテナ要素に指定して要素”間”のマージンを指定出来る。
従来のmargin-rightでは必要だった最後の要素には付けないなどの工夫が不要になる!
なお2列の要素の縦の間隔にも使える。flexでも使える。
grid-template-columns: repeat(<繰り返し回数>, <列の幅サイズ>)
コンテナ要素に指定して、repeat()
内の第1引数に列の数、第2引数に各列のサイズを渡すことで等間隔のgridの列を引くことが出来る。
今回第二引数に渡している”1fr”のfr
は、固定で指定した幅やマージンを除いた部分をよしなに埋めてくれるgridと一緒に登場した便利な単位。
gridTemplateColumns: `repeat(3, 1fr)`,
よってこの指定の意味は以下になる
画面幅に関わらず、gapやpadding, marginを除いた幅を3等分した位置で画面を分割する。
grid-auto-flow: dense
要素ごとのサイズにばらつきがある時に、なるべくコンテナ内に綺麗に並ぶようにしてくれる。
ご覧のように限界はあるが、なるべく左上に向けて要素を詰めて表示されている。
※要素の順番はバラバラになるので注意!
object-fit: cover;
object-fitを画像を囲んだ要素に指定することで、画像のサイズや縦横比がどこを基準にするか決められる
cover
は縦横比を保ちつつ、要素内に余白が出ないようにサイズを調整する(=縦と横で幅が広い方の長さに合わせて、要素に入りきらない部分は枠外にはみ出す)
今回は外部APIから取得した画像なので使えなかったけど、この辺の操作はbackground-imageで指定して、その関連プロパティを使った方が扱いやすい気もする
余談
gridを20列にして、細かく縦横比を指定したらもっとカッコ良くなるかと思ったら、grid-auto-flow: dense
が上手くかみ合わなくてむしろ見た目が悪くなってしまった><