はじめに#
現代のWebサイトでは、スマートフォンからRetinaディスプレイ搭載のデスクトップPCまで、多様なデバイスで画像を表示する必要があります。しかし、単一の画像ファイルをすべてのデバイスに配信すると、モバイルユーザーには不要に大きなファイルをダウンロードさせ、高解像度ディスプレイでは画像がぼやけて表示されるという問題が発生します。
レスポンシブ画像技術を使えば、デバイスの画面サイズや解像度に応じて最適な画像を配信し、パフォーマンスとユーザー体験の両方を向上させることができます。
本記事では、レスポンシブ画像について以下の内容を解説します。
- レスポンシブ画像が必要な理由と解決すべき課題
srcsetとsizes属性による解像度切り替え
picture要素によるアートディレクションの実装
object-fitとobject-positionによる画像フィッティング
- 実務で使えるレスポンシブ画像パターン
前提条件#
本記事を読み進めるにあたり、以下の知識があることを前提としています。
- HTMLの基本的なタグ構造
- CSSの基本構文(セレクタ・プロパティ・値)
- レスポンシブデザインとメディアクエリの基本概念
動作確認環境#
- Google Chrome 131以降
- Firefox 133以降
- Safari 18以降
- Microsoft Edge 131以降
レスポンシブ画像が必要な理由#
従来の<img>タグでは、1つの画像ファイルをすべてのデバイスに配信します。この方法には大きく2つの問題があります。
解像度切り替え問題(Resolution Switching)#
小さな画面のスマートフォンに対して、デスクトップ向けの大きな画像を配信すると、以下の問題が発生します。
| 問題 |
影響 |
| 帯域幅の無駄 |
モバイルユーザーが不要に大きなファイルをダウンロード |
| 読み込み時間の増加 |
ページ表示速度が低下し、離脱率が上昇 |
| データ通信量の増加 |
モバイルデータプランを圧迫 |
逆に、小さな画像をデスクトップの高解像度ディスプレイで表示すると、画像がぼやけて表示品質が低下します。
アートディレクション問題(Art Direction)#
横長の風景写真をそのままスマートフォンで表示すると、被写体が小さくなりすぎて何が写っているか分からなくなることがあります。デバイスに応じて、画像の構図自体を変える必要があるケースです。
flowchart LR
subgraph problems["レスポンシブ画像の2大課題"]
direction TB
A["解像度切り替え問題<br/>(Resolution Switching)"]
B["アートディレクション問題<br/>(Art Direction)"]
end
A --> C["同じ画像を<br/>異なるサイズで配信"]
B --> D["異なる構図の画像を<br/>条件に応じて配信"]srcset属性による解像度切り替え#
srcset属性を使うと、ブラウザに対して複数の画像ファイルを提供し、デバイスの条件に応じて最適な画像を選択させることができます。
基本的な構文#
1
2
3
4
5
6
7
8
9
10
|
<img
srcset="image-480w.jpg 480w,
image-800w.jpg 800w,
image-1200w.jpg 1200w"
sizes="(max-width: 600px) 480px,
(max-width: 1000px) 800px,
1200px"
src="image-800w.jpg"
alt="サンプル画像"
/>
|
この例では、3つの異なるサイズの画像を用意し、ブラウザが自動的に最適な画像を選択します。
srcset属性の記述方法#
srcset属性には、カンマ区切りで画像情報を記述します。各画像情報は以下の形式です。
画像ファイルパス 幅記述子
幅記述子(width descriptor)は画像の実際の幅をピクセル単位で指定し、単位にはwを使用します(pxではありません)。
1
2
3
4
5
6
7
|
<img
srcset="small.jpg 480w,
medium.jpg 800w,
large.jpg 1200w"
src="medium.jpg"
alt="サンプル画像"
/>
|
| 記述 |
意味 |
small.jpg 480w |
幅480ピクセルの画像 |
medium.jpg 800w |
幅800ピクセルの画像 |
large.jpg 1200w |
幅1200ピクセルの画像 |
sizes属性による表示サイズのヒント#
sizes属性は、特定のメディア条件下で画像がどのくらいの幅で表示されるかをブラウザに伝えます。ブラウザはこの情報を元に、srcsetから最適な画像を選択します。
1
2
3
4
5
6
7
8
9
10
|
<img
srcset="image-480w.jpg 480w,
image-800w.jpg 800w,
image-1200w.jpg 1200w"
sizes="(max-width: 600px) 100vw,
(max-width: 1000px) 50vw,
800px"
src="image-800w.jpg"
alt="サンプル画像"
/>
|
上記のsizes属性は以下のように解釈されます。
| メディア条件 |
画像の表示幅 |
| ビューポート幅600px以下 |
ビューポート幅の100% |
| ビューポート幅1000px以下 |
ビューポート幅の50% |
| それ以外 |
800px固定 |
sizes属性ではパーセント(%)は使用できませんが、vw単位やピクセル値、calc()関数は使用できます。
ブラウザの画像選択プロセス#
ブラウザが画像を選択するプロセスを理解しておくと、適切な設定ができるようになります。
flowchart TB
A["ページ読み込み開始"] --> B["sizes属性を評価<br/>表示スロットサイズを決定"]
B --> C["デバイスのピクセル密度を確認"]
C --> D["必要な画像幅を計算<br/>(スロットサイズ × ピクセル密度)"]
D --> E["srcsetから最適な画像を選択"]
E --> F["画像をダウンロード・表示"]例えば、ビューポート幅500pxのデバイス(ピクセル密度2x)で上記のコードを読み込んだ場合、以下のように処理されます。
sizesの(max-width: 600px) 100vwが適用され、表示スロットは500px
- ピクセル密度2xを考慮すると、必要な画像幅は1000px
srcsetから1000pxに最も近いimage-1200w.jpgが選択される
ピクセル密度記述子(x記述子)#
画像が常に同じサイズで表示される場合は、幅記述子の代わりにピクセル密度記述子(x記述子)を使用できます。
1
2
3
4
5
6
7
8
9
|
<img
srcset="logo.png 1x,
logo-2x.png 2x,
logo-3x.png 3x"
src="logo.png"
alt="ロゴ"
width="200"
height="50"
/>
|
| 記述子 |
対象デバイス |
1x |
標準解像度ディスプレイ |
2x |
Retinaディスプレイ(2倍密度) |
3x |
超高解像度ディスプレイ(3倍密度) |
この方法はsizes属性が不要でシンプルですが、画像の表示サイズが固定される場合にのみ使用できます。
picture要素によるアートディレクション#
picture要素を使うと、メディア条件に応じて異なる構図の画像を表示できます。これにより、アートディレクション問題を解決できます。
基本的な構文#
1
2
3
4
5
|
<picture>
<source media="(max-width: 799px)" srcset="portrait.jpg" />
<source media="(min-width: 800px)" srcset="landscape.jpg" />
<img src="landscape.jpg" alt="風景写真" />
</picture>
|
picture要素は以下の要素で構成されます。
| 要素 |
役割 |
<picture> |
コンテナ要素。子要素をラップ |
<source> |
条件付きの画像ソースを定義 |
<img> |
フォールバック画像とalt属性を提供(必須) |
ブラウザはsource要素を上から順に評価し、最初にマッチした条件の画像を使用します。どの条件にもマッチしない場合、img要素の画像が使用されます。
メディア条件による切り替え#
source要素のmedia属性にメディアクエリを指定することで、画面サイズや向きに応じて画像を切り替えられます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<picture>
<!-- モバイル向け:縦長のクローズアップ画像 -->
<source
media="(max-width: 599px)"
srcset="hero-mobile.jpg"
/>
<!-- タブレット向け:中間サイズの画像 -->
<source
media="(max-width: 1023px)"
srcset="hero-tablet.jpg"
/>
<!-- デスクトップ向け:横長のワイド画像 -->
<img src="hero-desktop.jpg" alt="ヒーローイメージ" />
</picture>
|
画像フォーマットによる切り替え#
picture要素は、ブラウザがサポートする画像フォーマットに応じて最適なフォーマットを提供するためにも使用できます。
1
2
3
4
5
6
7
8
|
<picture>
<!-- AVIFをサポートするブラウザ向け -->
<source type="image/avif" srcset="photo.avif" />
<!-- WebPをサポートするブラウザ向け -->
<source type="image/webp" srcset="photo.webp" />
<!-- フォールバック:すべてのブラウザで対応 -->
<img src="photo.jpg" alt="写真" />
</picture>
|
現代の画像フォーマットは、従来のJPEGやPNGと比較して大幅にファイルサイズを削減できます。
| フォーマット |
圧縮効率 |
ブラウザサポート |
| AVIF |
非常に高い |
Chrome、Firefox、Safari(一部) |
| WebP |
高い |
すべてのモダンブラウザ |
| JPEG |
標準 |
すべてのブラウザ |
アートディレクションとレスポンシブの組み合わせ#
source要素でもsrcsetとsizes属性を使用できるため、アートディレクションと解像度切り替えを組み合わせることが可能です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<picture>
<source
media="(max-width: 599px)"
srcset="hero-mobile-480w.jpg 480w,
hero-mobile-800w.jpg 800w"
sizes="100vw"
/>
<source
media="(min-width: 600px)"
srcset="hero-desktop-800w.jpg 800w,
hero-desktop-1200w.jpg 1200w,
hero-desktop-1600w.jpg 1600w"
sizes="(max-width: 1200px) 100vw, 1200px"
/>
<img src="hero-desktop-1200w.jpg" alt="ヒーローイメージ" />
</picture>
|
ダークモード対応#
prefers-color-schemeメディア機能を使用すると、ユーザーのカラースキーム設定に応じて画像を切り替えられます。
1
2
3
4
5
6
7
8
9
10
11
|
<picture>
<source
media="(prefers-color-scheme: dark)"
srcset="logo-dark.png"
/>
<source
media="(prefers-color-scheme: light)"
srcset="logo-light.png"
/>
<img src="logo-light.png" alt="サービスロゴ" />
</picture>
|
object-fitによる画像フィッティング#
object-fitプロパティは、<img>や<video>などの**置換要素(replaced element)**のコンテンツを、指定されたコンテナサイズ内にどのようにフィットさせるかを制御します。
基本的な構文#
1
2
3
4
5
|
img {
width: 300px;
height: 200px;
object-fit: cover;
}
|
object-fitの値#
object-fitプロパティには5つの値があります。
flowchart TB
subgraph objectfit["object-fitの値"]
direction LR
A["fill<br/>(デフォルト)"]
B["contain"]
C["cover"]
D["none"]
E["scale-down"]
end各値の詳細は以下の通りです。
| 値 |
動作 |
ユースケース |
fill |
アスペクト比を無視してコンテナを埋める |
ほぼ使用しない(画像が歪む) |
contain |
アスペクト比を維持し、全体が収まるようにスケール |
サムネイル、アイコン |
cover |
アスペクト比を維持し、コンテナを完全に覆う |
ヒーロー画像、カード画像 |
none |
リサイズしない(元のサイズで表示) |
特殊なケース |
scale-down |
noneとcontainの小さい方を適用 |
サイズ上限のある画像 |
各値の動作比較#
以下のCSSで各値の違いを確認できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
.image-container {
width: 300px;
height: 200px;
border: 2px solid #333;
}
/* fill: アスペクト比を無視して引き伸ばす */
.fill {
object-fit: fill;
}
/* contain: 全体が収まるようにスケール(余白が生じる可能性) */
.contain {
object-fit: contain;
}
/* cover: コンテナを完全に覆う(はみ出た部分は切り取り) */
.cover {
object-fit: cover;
}
/* none: 元のサイズで表示(はみ出る可能性) */
.none {
object-fit: none;
}
/* scale-down: noneとcontainの小さい方 */
.scale-down {
object-fit: scale-down;
}
|
実践的なobject-fitの使用例#
カード型UIで画像を統一サイズで表示する場合の例です。
1
2
3
4
5
6
7
8
9
10
11
|
<div class="card">
<img
src="product.jpg"
alt="商品画像"
class="card-image"
/>
<div class="card-content">
<h3>商品名</h3>
<p>商品の説明文</p>
</div>
</div>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
.card {
width: 300px;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.card-image {
width: 100%;
height: 200px;
object-fit: cover;
display: block;
}
.card-content {
padding: 16px;
}
|
object-positionによる配置制御#
object-positionプロパティは、object-fitで切り取りが発生する場合に、画像のどの部分を表示するかを制御します。
基本的な構文#
1
2
3
4
5
6
|
img {
width: 300px;
height: 200px;
object-fit: cover;
object-position: center top; /* 上部中央を基準に配置 */
}
|
object-positionの指定方法#
object-positionはbackground-positionと同様の方法で指定できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/* キーワードによる指定 */
object-position: center; /* 中央(デフォルト) */
object-position: top; /* 上端 */
object-position: right bottom; /* 右下 */
object-position: left center; /* 左端中央 */
/* パーセンテージによる指定 */
object-position: 50% 50%; /* 中央 */
object-position: 0% 0%; /* 左上 */
object-position: 100% 100%; /* 右下 */
object-position: 25% 75%; /* 左から25%、上から75%の位置 */
/* 長さによる指定 */
object-position: 10px 20px; /* 左から10px、上から20pxの位置 */
object-position: 1rem 2rem;
|
人物写真の顔を中心に表示する#
人物写真をカードに表示する際、顔が切れてしまうことがあります。object-positionで調整できます。
1
2
3
4
5
6
7
|
.profile-image {
width: 150px;
height: 150px;
border-radius: 50%;
object-fit: cover;
object-position: center 20%; /* 上寄りに配置して顔を中心に */
}
|
aspect-ratioとの組み合わせ#
aspect-ratioプロパティと組み合わせることで、より柔軟なレスポンシブ画像レイアウトを実現できます。
1
2
3
4
5
6
|
.responsive-image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
object-position: center center;
}
|
この方法では、画像の幅が変化しても常に16:9のアスペクト比を維持しながら、画像がコンテナを完全に覆います。
実践的なレスポンシブ画像パターン#
パターン1: ヒーローセクション#
画面幅に応じて構図と解像度の両方を切り替えるヒーロー画像の実装例です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
<picture>
<!-- モバイル: 縦長構図 -->
<source
media="(max-width: 767px)"
srcset="hero-mobile-400w.webp 400w,
hero-mobile-600w.webp 600w,
hero-mobile-800w.webp 800w"
sizes="100vw"
type="image/webp"
/>
<source
media="(max-width: 767px)"
srcset="hero-mobile-400w.jpg 400w,
hero-mobile-600w.jpg 600w,
hero-mobile-800w.jpg 800w"
sizes="100vw"
/>
<!-- デスクトップ: 横長構図 -->
<source
media="(min-width: 768px)"
srcset="hero-desktop-800w.webp 800w,
hero-desktop-1200w.webp 1200w,
hero-desktop-1920w.webp 1920w"
sizes="100vw"
type="image/webp"
/>
<source
media="(min-width: 768px)"
srcset="hero-desktop-800w.jpg 800w,
hero-desktop-1200w.jpg 1200w,
hero-desktop-1920w.jpg 1920w"
sizes="100vw"
/>
<img
src="hero-desktop-1200w.jpg"
alt="サービスのメインビジュアル"
class="hero-image"
/>
</picture>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
.hero-image {
width: 100%;
height: 60vh;
min-height: 400px;
max-height: 800px;
object-fit: cover;
object-position: center center;
}
@media (max-width: 767px) {
.hero-image {
height: 80vh;
object-position: center top;
}
}
|
パターン2: 商品グリッド#
ECサイトなどで使用する商品画像グリッドの実装例です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<div class="product-grid">
<article class="product-card">
<img
srcset="product1-200w.jpg 200w,
product1-400w.jpg 400w,
product1-600w.jpg 600w"
sizes="(max-width: 599px) calc(50vw - 24px),
(max-width: 899px) calc(33.33vw - 32px),
280px"
src="product1-400w.jpg"
alt="商品名"
class="product-image"
loading="lazy"
/>
<h3 class="product-name">商品名</h3>
<p class="product-price">¥1,980</p>
</article>
<!-- 他の商品... -->
</div>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 24px;
padding: 16px;
}
.product-card {
background: #fff;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}
.product-image {
width: 100%;
aspect-ratio: 1 / 1;
object-fit: contain;
background: #f5f5f5;
}
.product-name {
padding: 12px 16px 4px;
font-size: 1rem;
margin: 0;
}
.product-price {
padding: 0 16px 16px;
font-size: 1.25rem;
font-weight: bold;
color: #e44;
margin: 0;
}
|
パターン3: 背景画像のレスポンシブ対応#
CSSのbackground-imageでもレスポンシブ対応が可能です。image-set()関数を使用します。
1
2
3
4
5
6
7
8
9
10
11
|
.hero-section {
background-image: url('hero-1x.jpg');
background-image: image-set(
url('hero-1x.webp') 1x type('image/webp'),
url('hero-2x.webp') 2x type('image/webp'),
url('hero-1x.jpg') 1x,
url('hero-2x.jpg') 2x
);
background-size: cover;
background-position: center center;
}
|
パフォーマンス最適化のベストプラクティス#
遅延読み込み(Lazy Loading)#
ファーストビュー外の画像にはloading="lazy"属性を追加して、遅延読み込みを有効にします。
1
2
3
4
5
6
7
8
|
<img
srcset="image-480w.jpg 480w,
image-800w.jpg 800w"
sizes="(max-width: 600px) 100vw, 800px"
src="image-800w.jpg"
alt="コンテンツ画像"
loading="lazy"
/>
|
優先読み込み(Priority Hints)#
ファーストビューの重要な画像にはfetchpriority="high"を指定して、優先的に読み込ませます。
1
2
3
4
5
|
<img
src="hero-image.jpg"
alt="ヒーローイメージ"
fetchpriority="high"
/>
|
画像サイズの明示#
CLS(Cumulative Layout Shift)を防ぐため、画像のサイズを明示的に指定します。
1
2
3
4
5
6
7
8
9
|
<img
srcset="image-480w.jpg 480w,
image-800w.jpg 800w"
sizes="(max-width: 600px) 100vw, 800px"
src="image-800w.jpg"
alt="サンプル画像"
width="800"
height="600"
/>
|
または、CSSでaspect-ratioを指定します。
1
2
3
4
5
|
.responsive-image {
width: 100%;
height: auto;
aspect-ratio: 4 / 3;
}
|
推奨される画像サイズのバリエーション#
一般的なユースケースでは、以下のサイズバリエーションを用意することを推奨します。
| 用途 |
推奨サイズ |
| モバイル向け |
320w, 480w, 640w |
| タブレット向け |
768w, 1024w |
| デスクトップ向け |
1280w, 1600w, 1920w |
| Retinaディスプレイ |
各サイズの2倍(例: 2560w, 3840w) |
まとめ#
本記事では、レスポンシブ画像の実装に必要な技術について解説しました。
| 技術 |
解決する問題 |
主なユースケース |
srcset + sizes |
解像度切り替え |
同じ構図で異なるサイズの画像を配信 |
picture + source |
アートディレクション |
条件に応じて構図の異なる画像を配信 |
object-fit |
画像フィッティング |
コンテナ内での画像表示方法を制御 |
object-position |
配置制御 |
切り取り時の表示位置を調整 |
レスポンシブ画像を適切に実装することで、以下のメリットが得られます。
- パフォーマンス向上: デバイスに最適化された画像サイズでデータ転送量を削減
- 表示品質の向上: 高解像度ディスプレイでも鮮明な画像を表示
- ユーザー体験の向上: すべてのデバイスで最適な構図の画像を表示
- Core Web Vitals改善: LCP(Largest Contentful Paint)スコアの向上
これらの技術を組み合わせることで、あらゆるデバイスで最適な画像表示を実現し、Webサイトのパフォーマンスとユーザー体験を大幅に向上させることができます。
参考リンク#