CSSで画像を決まった比率にトリミングするには、aspect-ratio で枠の縦横比を決め、object-fit: cover で画像を枠いっぱいに縮小・拡大して、はみ出した部分を切り取ります。 切り取る位置は object-position で調整します。object-fit は img や video のような「置換要素」に効くプロパティで、div の背景画像には効きません(背景は background-size を使います)。

この記事では、まず object-fit の5つの値と aspect-ratio の早見表を示し、縦長・横長・正方形・小さい画像の4種類を作業フォルダで作り、Playwright(Chromium 141.0.7390.37)で表示サイズ・余白・切り取られた辺を実測した比較表を載せます。画像はここに掲載できないため、結果はすべて数値で示しています。そのうえで、カードのサムネイル・正方形のアバター・ヒーロー画像・16:9 の iframe・ギャラリーのグリッドの5パターンをコピペできる形でまとめ、効かない原因と background-size・picture 要素との使い分けまで解説します。

object-fit・aspect-ratio の早見表

指定 画像の縦横比 枠を埋める はみ出し 余白 主な用途
object-fit: fill(初期値) 崩れる(引き伸ばす) 埋める なし なし 比率が同じと分かっている画像
object-fit: contain 保つ 長い辺だけ合わせる なし 出る(上下か左右) ロゴ・商品の全体を見せたい画像
object-fit: cover 保つ 埋める 出る(切り取る) なし サムネイル・アバター・ヒーロー
object-fit: none 保つ 元のサイズのまま 大きい画像は出る 小さい画像は出る 拡大縮小させたくない画像
object-fit: scale-down 保つ none と contain の小さい方 なし 出る 小さい画像を拡大させたくないとき
object-position: 50% 50%(初期値) ― ― 中央を残す 中央に寄せる ―
object-position: top ― ― 上を残す 上に寄せる 人物の顔が上にある写真
aspect-ratio: 16 / 9 枠の比率を決める ― ― ― 動画・ヒーロー
aspect-ratio: 1 正方形の枠 ― ― ― アバター・ギャラリー
aspect-ratio: auto 1200 / 600 画像読み込み後は画像の比率 ― ― ― width/height 属性と同じ働き

迷ったら「枠に aspect-ratio、画像に width: 100%; height: 100%; object-fit: cover;」の組み合わせを使えば、どんな比率の画像でも枠いっぱいにそろいます。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「CSS object-fit・aspect-ratio|画像トリミング5パターン」

object-fit の5つの値を実測で比べる

同じ 300×200px の枠に、比率の違う4枚の画像を入れ、どう表示されるかを測りました。 画像はすべて緑の塗りつぶしで、四辺に赤い枠線を描いてあります。表示後に赤い枠線が見えている辺は「切り取られていない辺」、見えない辺は「切り取られた辺」です。枠の背景はマゼンタにして、余白の幅を数えています。

画像(元のサイズ) 比率
縦長 portrait.png(600×900) 2:3
横長 landscape.png(1200×600) 2:1
正方形 square.png(500×500) 1:1
小さい small.png(120×80) 3:2(枠と同じ)

実測結果(枠 300×200px)

画像 fill contain cover none scale-down
縦長 600×900 300×200(縦に縮む)・四辺見える 134×200・余白 左右83px 300×200・上下が切れる 300×200・四辺とも切れる 134×200・余白 左右83px
横長 1200×600 300×200(横に縮む)・四辺見える 300×150・余白 上下25px 300×200・左右が切れる 300×200・四辺とも切れる 300×150・余白 上下25px
正方形 500×500 300×200・四辺見える 200×200・余白 左右50px 300×200・上下が切れる 300×200・四辺とも切れる 200×200・余白 左右50px
小さい 120×80 300×200(2.5倍に拡大) 300×200(拡大) 300×200(拡大) 120×80・余白 上下60px 左右90px 120×80・余白 上下60px 左右90px

表の「134×200」などは、スクリーンショットの画素を数えた「画像が描かれている範囲」です。

はみ出した量の計算(cover・none)

cover は「枠を埋められる最小の倍率」で縮小・拡大します。倍率は max(枠の幅 ÷ 画像の幅, 枠の高さ ÷ 画像の高さ) です。上の実測(切れた辺)と、計算したはみ出し量は一致しています。

画像 cover の倍率 描画サイズ(計算) 切り取られる量(計算) 実測で切れた辺
縦長 600×900 max(0.5, 0.222) = 0.5 300×450 上下 125px ずつ 上下
横長 1200×600 max(0.25, 0.333) = 0.333 400×200 左右 50px ずつ 左右
正方形 500×500 max(0.6, 0.4) = 0.6 300×300 上下 50px ずつ 上下
小さい 120×80 max(2.5, 2.5) = 2.5 300×200 なし なし(四辺見える)

contain は逆に min(...) の倍率で、縦長なら 0.222 倍の 133.3×200px(実測 134×200)になり、左右に約83pxの余白ができます。小さい画像を引き伸ばしたくないときは scale-down を使うと、実測のとおり元の 120×80 のまま中央に置かれます。

object-position で残す位置を決める

縦長画像(600×900)を cover で 300×200 に入れ、object-position を変えたときの、見えている辺を調べました。

object-position 見えている赤枠の辺 残る部分
50% 50%(初期値) 左・右 中央(上下125pxずつ切れる)
top 上・左・右 上端から(下が250px切れる)
left top 上・左・右 上端から(縦長なので左右は元から収まる)
right 20% bottom 0 下・左・右 下端から

人物写真やキャラクターのように「上に大事なものがある」画像は object-position: top、または 50% 25% のように少し上を残す指定が合います。

計測に使ったコード

# make_images.py(Python 3.11 + Pillow)
# 実行: python3 make_images.py
from PIL import Image, ImageDraw

sizes = {"portrait": (600, 900), "landscape": (1200, 600), "square": (500, 500), "small": (120, 80)}
for name, (w, h) in sizes.items():
    im = Image.new("RGB", (w, h), (0, 160, 0))          # 緑で塗る
    d = ImageDraw.Draw(im)
    b = max(4, w // 40)                                  # 枠線の太さ
    d.rectangle([0, 0, w - 1, b - 1], fill=(255, 0, 0))  # 上
    d.rectangle([0, h - b, w - 1, h - 1], fill=(255, 0, 0))  # 下
    d.rectangle([0, 0, b - 1, h - 1], fill=(255, 0, 0))  # 左
    d.rectangle([w - b, 0, w - 1, h - 1], fill=(255, 0, 0))  # 右
    im.save(f"{name}.png")
// of.js(Node 22 + playwright 1.56.0、既存の Chromium を使用)
// 実行: node of.js → of/ に枠ごとのスクリーンショットを保存
const { chromium } = require('playwright');
const imgs = ['portrait', 'landscape', 'square', 'small'];
const fits = ['fill', 'contain', 'cover', 'none', 'scale-down'];

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage({ viewport: { width: 400, height: 300 } });
  for (const im of imgs) for (const f of fits) {
    await page.setContent(`
      <style>
        body { margin: 0 }
        img { display: block; width: 300px; height: 200px; background: #f0f; object-fit: ${f} }
      </style>
      <img src="file://${process.cwd()}/${im}.png">`);
    await page.waitForFunction(() => document.images[0].complete);
    await page.locator('img').screenshot({ path: `of/${im}_${f}.png` });
  }
  await browser.close();
})();

スクリーンショットは Pillow で「マゼンタ以外の画素の範囲(=画像の描画範囲)」と「各辺の中央に赤い画素があるか」を数えて、上の表にしました。

aspect-ratio の書き方

aspect-ratio は要素の「幅:高さ」を決めるプロパティです。 幅だけを決めれば、高さは比率から自動で計算されます。

.box-16x9 { aspect-ratio: 16 / 9; }  /* 16:9 */
.box-4x3  { aspect-ratio: 4 / 3; }   /* 4:3 */
.box-sq   { aspect-ratio: 1; }       /* 1:1(1 / 1 と同じ) */
.box-auto { aspect-ratio: auto 3 / 2; } /* 中身(画像)に比率があればそれを使い、読み込み前は 3:2 */
指定(実測:Chromium 141) 結果
img { width: 320px; aspect-ratio: 16/9; } 320×180
img { width: 320px; height: 100px; aspect-ratio: 16/9; } 320×100(幅と高さを両方指定すると比率は使われない)
div { width: 320px; aspect-ratio: 16/9; min-height: 300px; } 320×300(min-height が優先され比率が崩れる)
div { width: 200px; aspect-ratio: 1; } に長い文章 200×630(中身が多いと高さが伸びる)
同じ div に min-height: 0; overflow: auto; 200×200(比率を保ち、中はスクロール)

aspect-ratio は「最低限この比率」という意味に近く、div に文字を入れると中身に合わせて高さが伸びます。 比率を必ず守りたいときは min-height: 0 と overflow: auto(または hidden)を一緒に指定します。画像(img)はもともと中身で伸びないので、この心配はありません。

コピペで使える画像トリミング5パターン

どのパターンも「枠の比率」と「画像の埋め方」を分けて書くのがコツです。 下のコードはそのまま貼って使えます。

1. カードのサムネイル(16:9)

<article class="card">
  <img class="card__thumb" src="article.jpg" alt="記事の見出し画像" width="1200" height="675" loading="lazy">
  <h3 class="card__title">記事タイトル</h3>
</article>
.card__thumb {
  display: block;           /* 下の隙間(約4px)をなくす */
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: 50% 40%; /* 少し上を残す */
  border-radius: 8px;
}

display: block は、インラインの画像の下にできる隙間を消すためです。 実測でも、display を指定しない 300×200 の画像では親の高さが 205px になり、約5pxの隙間ができました。

2. 正方形のアバター(丸)

<img class="avatar" src="member.jpg" alt="山田さんのアイコン" width="96" height="96">
.avatar {
  width: 48px;
  aspect-ratio: 1;
  height: auto;
  object-fit: cover;
  object-position: top;   /* 顔が上にある写真が多いので上を残す */
  border-radius: 50%;
}

縦長の写真でも、上の実測のとおり上下が切り取られて正方形になります。 object-position: top で頭が切れにくくなります。

3. ヒーロー画像(画面幅いっぱい・高さに上限)

<section class="hero">
  <img class="hero__img" src="hero.jpg" alt="" width="1920" height="800" fetchpriority="high">
  <h1 class="hero__title">イベントのお知らせ</h1>
</section>
.hero {
  position: relative;
  aspect-ratio: 12 / 5;      /* 1920:800 */
  max-height: 70vh;
  width: 100%;
}
.hero__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero__title {
  position: relative;        /* 画像の上に文字を重ねる */
  margin: 0;
  padding: 24px;
  color: #fff;
}

親に比率(aspect-ratio)と上限(max-height)を持たせ、画像は親の大きさいっぱいに cover で広げます。 画面が横に広いときは上限の 70vh で止まり、はみ出した上下が切り取られます。飾りの画像なので alt="" にしています。ファーストビューの画像には fetchpriority="high" を付け、loading="lazy" は付けません。

4. YouTube などの iframe を 16:9 にする

<div class="video">
  <iframe src="https://www.youtube.com/embed/動画ID" title="紹介動画" loading="lazy"
          allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
          allowfullscreen></iframe>
</div>
.video iframe {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  border: 0;
}
指定(親の幅 640px、実測) iframe のサイズ
width: 100% のみ 640×150(iframe の初期の高さ 150px のまま)
width: 100%; height: auto; aspect-ratio: 16/9; 640×360

iframe は object-fit の対象外ですが、aspect-ratio は効きます。 以前よく使われた padding-top: 56.25% の書き方は不要です。

5. ギャラリーのグリッド(正方形タイル)

<ul class="gallery">
  <li><img src="item-01.jpg" alt="備品の写真1" width="800" height="1200" loading="lazy"></li>
  <li><img src="item-02.jpg" alt="備品の写真2" width="1200" height="600" loading="lazy"></li>
  <li><img src="item-03.jpg" alt="備品の写真3" width="600" height="600" loading="lazy"></li>
</ul>
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 8px;
  padding: 0;
  list-style: none;
}
.gallery img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
}

縦長・横長・正方形が混ざっていても、すべて同じ大きさの正方形タイルにそろいます。 全体を見せたい(切り取りたくない)ギャラリーなら、object-fit: contain にして background で余白の色を付けます。

width/height 属性によるレイアウトのずれ対策

img に width と height 属性を書いておくと、ブラウザは画像の読み込み前から比率を知り、表示領域を確保できます。 CSS で height: auto にしていても、属性の値から aspect-ratio: auto 1200 / 600 に相当する比率が使われます。

画像の応答を1.5秒遅らせ、幅400pxの枠で「読み込み前」と「読み込み後」の高さを測りました。

img の書き方 読み込み前の高さ 読み込み前の「次の段落」の位置 読み込み後の高さ ずれ
属性なし 0px y=0 200px 200px 下にずれる
width="1200" height="600" 200px y=200 200px なし
<!-- 属性は「元の画像のサイズ」をそのまま書く。表示サイズは CSS で決める -->
<img src="landscape.png" width="1200" height="600" alt="倉庫の棚の写真"
     style="max-width: 100%; height: auto; display: block;">

属性に書くのは表示サイズではなく元の画像のピクセル数(比率が合っていれば可)です。 CSS 側で height: auto を忘れると、属性の 600px がそのまま高さになるので、max-width: 100%; height: auto; をセットにします。Core Web Vitals 全体の改善方法は別記事で扱っているため、ここでは画像の比率に関わる要点だけにしています。

object-fit・aspect-ratio が効かない原因

効かないときは「対象が img・video か」「高さが決まっているか」「比率より強い指定がないか」の3点を確認します。

症状 原因 実測(Chromium 141) 直し方
div に object-fit: cover を付けても変わらない object-fit は置換要素(img・video など)専用 300×200 の div の背景画像は元の大きさのまま並ぶ background-size: cover を使う
iframe に object-fit が効かない iframe・embed は対象外(MDN) ― aspect-ratio で枠の比率を決める
height: 100% の画像が親に収まらない 親の高さが決まっていない(auto) 親 width 300px・高さ指定なし → 画像 300×150(元の比率のまま) 親に height か aspect-ratio を指定
親に高さを付けたら効いた ― 親 height 200px → 画像 300×200 ―
親に aspect-ratio: 4/3 ― 親 300×225 → 画像 300×225 ―
比率が反映されない width と height を両方指定している 320×100(16/9 は無視) どちらかを auto に
比率より高くなる min-height が比率より大きい 320×300(16/9 なら180のはず) min-height を外すか、メディアクエリで調整
div の比率が崩れる 中身の文字が多い 200×630 min-height: 0; overflow: auto;
画像の下に隙間ができる 画像がインライン要素でベースラインに並ぶ 親の高さ 205px(画像は200px) display: block または vertical-align: bottom
小さい画像がぼやける cover・contain は拡大もする 120×80 → 300×200(2.5倍) scale-down か、元画像を大きくする

background-size との使い分け

background-size: cover / contain は背景画像版の object-fit です。 見た目は同じようにできますが、HTMLとしての意味が変わります。

観点 img + object-fit background-image + background-size
画像の意味 内容の一部(alt で説明できる) 飾り(読み上げされない)
alt 書ける 書けない
loading="lazy"・srcset・fetchpriority 使える 使えない(image-set() で解像度切り替えは可)
印刷 印刷される 設定によっては印刷されない
重ねる・複数枚 要素を重ねる background に複数指定できる
向いている用途 記事・商品・人物の写真 模様・飾り・グラデーションとの重ね

内容として意味がある画像は img + object-fit、飾りは background-size と分けると、アクセシビリティと読み込み速度の両方で有利です。

picture 要素との組み合わせ

picture 要素で画面幅ごとに画像を切り替える場合も、object-fit は中の img に指定します。 source は画像の候補を並べるだけで、スタイルは最終的に表示される img に適用されます。

<picture class="hero-picture">
  <!-- スマホ:縦長に切り出した画像 -->
  <source media="(max-width: 600px)" srcset="hero-sp.jpg" width="800" height="1000">
  <!-- PC:横長の画像 -->
  <img src="hero-pc.jpg" alt="新しい会員ページの紹介" width="1920" height="800">
</picture>
.hero-picture img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 12 / 5;
  object-fit: cover;
}
@media (max-width: 600px) {
  .hero-picture img { aspect-ratio: 4 / 5; }
}
方法 向いているとき
object-fit: cover + object-position だけ 1枚の画像で、切り取る位置を少し変えれば済むとき
picture + 画面幅ごとの画像 スマホとPCで構図そのものを変えたいとき(アートディレクション)
img の srcset + sizes 構図は同じで、解像度だけ切り替えたいとき

source にも width・height 属性を書けるので、画面幅ごとに比率が違う画像でも読み込み前に領域を確保できます。

よくある質問

Q. object-fit はすべてのブラウザで使えますか? A. はい。MDN では 2020年1月から主要ブラウザで使える「Baseline(Widely available)」とされています。aspect-ratio も現在の主要ブラウザで使えます。

Q. cover で切り取られた部分は読み込まれていないのですか? A. 画像全体が読み込まれ、表示のときに切り取られるだけです。通信量を減らしたい場合は、srcset と sizes で表示サイズに合った画像を選ばせるか、あらかじめ切り出した画像を用意します。

Q. aspect-ratio と width/height 属性はどちらを使えばよいですか? A. 両方使うのがおすすめです。 属性は読み込み前の領域確保(元画像の比率)、CSS の aspect-ratio は表示したい枠の比率に使います。両方あるときは CSS の aspect-ratio が表示に使われます。

Q. video 要素にも object-fit は使えますか? A. 使えます。video も置換要素なので、背景動画を object-fit: cover で枠いっぱいに表示できます。

まとめ

  • 画像のトリミングは「枠に aspect-ratio、画像に object-fit: cover」の組み合わせが基本
  • contain は全体を見せて余白、cover は枠を埋めて切り取り、scale-down は小さい画像を拡大しない(実測で確認)
  • 切り取る位置は object-position。人物は top や 50% 25% が合う
  • object-fit は img・video 専用。div は background-size、iframe は aspect-ratio を使う
  • height: 100% の画像は親の高さが必要。親に height か aspect-ratio を指定する
  • width・height 属性で読み込み前の領域を確保すると、200px のずれがなくなった
  • 構図を変えたいときは picture、解像度だけなら srcset と使い分ける

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「CSS object-fit・aspect-ratio|画像トリミング5パターン」

株式会社RJCでは、画像表示の細かな仕組みを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?