HOME
画像リソースはページを表示するダウンロード容量の大半を占めます。画像を最適化することでファイルサイズの削減とパフォーマンスの改善をするためには、どのような最適化戦略を行えば良いのかを説明します。
画像の最適化とは、Web ページに配置された画像の読み込み速度を最適化することです。Webページにおいて画像のファイルサイズは HTML, CSS, Javascript のリソースと比較してもサイズが大きいため、最適化されていなければパフォーマンスに大きな影響を与えます。
画像はファイル数もサイズも増大する傾向にあります。記事で使用する画像の他にもロゴ、背景、アイキャッチ、アイコン、バナーなど、様々な画像が使われています。また、高解像度のデバイス向けにオリジナルスケールの 2 倍や 3 倍の解像度を持つ高品質な画像を用意するとファイルの数もサイズも増大します。
画像のファイル数やサイズの増大は通信コストを圧迫します。ファイル数は、 HTTP リクエストのオーバーヘッドとなり、ファイルサイズはダウンロードの時間が増大します。そのため、画像最適化戦略では画像のファイル数やサイズを最適化することで通信量を減らし、パフォーマンスの向上を目指します。
Web で使われる主要な画像形式には PNG、GIF、JPEG、SVG、WebP があります。それぞれの画像形式は特性が異なるため、画像を最適化するためには画像形式の選択が重要になります。例えば、カメラで撮影した写真には JPEG 形式が向いています。その他の画像形式でも表現することは可能ですが、ファイルサイズが大きくなるため注意が必要です。
形式 | 圧縮性能 | 可逆圧縮 | 非可逆圧縮 | 透過 | アニメーション |
---|---|---|---|---|---|
PNG | 高い | ◯ | ✕ | ◯ | ✕ |
GIF | 普通 | ◯ | ◯ | △ | ◯ |
JPEG | 高い | △ | ◯ | ✕ | ✕ |
SVG | 高い | ◯ | ✕ | ◯ | ◯ |
WebP | 非常に高い | ◯ | ◯ | ◯ | ◯ |
PNG (Portable Network Graphics) 形式は、圧縮アルゴリズムとして Deflate を採用しており、圧縮による画質の劣化のない可逆圧縮の画像形式です。透過についてもクロマキーによる透過指定や、8 ビットから 16 ビットのアルファチャンネルをサポートします。しかし、GIF と異なり PNG にはアニメーション機能はサポートされていません。MNG や APNG によってアニメーションを表現することが可能ですが、厳密には PNG とは別の画像形式になります。
GIF (Graphics Interchange Format) 形式は、256 色以下の画像を扱うことができ、画像形式の中では歴史が長く、標準的にサポートされています。可逆圧縮、非可逆圧縮、アニメーションをサポートします。ただし、透過についてはアルファチャンネルによる制御ではなく、透過・非透過のみの区別になります。近年では、256 色までしか対応していないため、使用されるケースはアニメーションや画像ビーコンなど、限定的になっています。
JPEG (Joint Photographic Experts Group) 形式は、写真などの画像によく使われる形式であり、一般的に非可逆圧縮の画像形式として知られます。仕様上は可逆圧縮もサポートしていますが、ほとんど普及していません。無圧縮のオリジナル画像では比較的ファイルサイズが大きくなりがちですが、圧縮率を上げるとファイルサイズが減少する代わりにノイズが生じます。
SVG (Scalable Vector Graphics) 形式は、XML ベースの、2 次元ベクターイメージ用の画像形式です。その他の画像形式がラスタ形式である点に対して、オブジェクトを座標で表現するベクター形式になります。そのため、画像を拡大縮小しても劣化しないメリットがある反面、単純なオブジェクトしか表現できず写真のような複雑な画像には向かないデメリットがあります。また、SVG でアニメーションを表現する場合は、CSS や Javascript で制御できます。
WebP 形式は、Google が開発しているオープンソースの静止画用の画像形式です。その他の画像形式と比べて良好なパフォーマンスを示しますが、競合各社の思惑もあってブラウザでの採用が進んでいない側面もあります。
Apple から高解像度の Retina ディスプレイが登場して以来、画像の粗さが出ないように高解像度用の画像を用意することが一般的になりました。画像サイズの最適化を説明する前に、画像と解像度の関係を明らかにしていきたいと思います。
そもそも解像度とは、ピクセルの集合体として表現されるビットマップ画像における画素の密度を示す数値です。つまり、画像を表現する格子の細かさを表し、一般的には 1 インチあたりの密度を解像度の単位として表現します。
解像度の単位は dpi (dots per inch) が使われることが多く、その他にも ppi (pixels per inch) や lpi (line per inch) などがあります。dpi はプリンター印刷などで利用される解像度の単位で、色情報を持たないピクセル (ドット) で表現されます。厳密にはそれぞれの単位は異なりますが、ディスプレイ上ではほぼ同義であるとされています。
ディスプレイ上に画像を表示する場合、デバイスピクセスと CSS ピクセルの違いについては注意が必要です。デバイスピクセルとは、ディスプレイの物理的なピクセル数であり、CSS ピクセルはブラウザで表示させるための擬似的なピクセル数です。
例えば、100 × 100 の画像を非 Retina ディスプレイで表示すると、そのまま 100 × 100 で表示されます。しかし、同じスクリーンサイズの Retina ディスプレイで表示すると、50 × 50 で表示されます。そのため、CSS ピクセルで 100 × 100 と指定すると、デバイスピクセルでは 200 × 200 に拡大することで期待通りの描画サイズに整えます。
ただし、2 倍の解像度の画像を用意していない場合、単純に拡大しただけになるため、画像は粗くなります。Retina 対応とする場合、2 倍の解像度の画像を用意して出し分ける必要があります。将来的、もしくはデバイスによっては 3 倍以上の解像度の画像を求められる場合があります。もしも、100 × 100 のデータが必要なデバイスに 300 × 300 の画像を送ってしまうと、単純計算で本来必要なデータの約 9 倍のデータ量になってしまいます。
このようなデバイスピクセルと CSS ピクセルの比率を DPR (Device Pixel Ratio) と呼びます。例えば、iPhone X の物理上のデバイスピクセルは 1125 × 2436 ですが、論理上の CSS ピクセル数は 375 × 812 となり、デバイスピクセル比は 3:1 の関係になります。つまり、画像の粗さを出さずにキレイな画像を表示するには 3 倍の解像度が必要になります。
画像を表示する場合、width, height
を定義することでサイズを指定できます。しかし、Retina などの高解像度のデバイスでもキレイな画像を表示するために、大きい画像を小さいサイズに縮小して表示しているケースがあります。画像の読み込みにおいてサイズの大きい画像を小さく表示することは、ネットワークの転送においても、ブラウザのレンダリングにおいてもオーバーヘッドになります。
サイズの最適化を行う場合、同じ画像でいくつかのファイルサイズを用意しておき、DPR によって出し分けます。DPR を考慮する場合、デバイスによってどのサイズの画像が最適であるかは ViewPort と dpi によって求めることができます。
ViewPort とはデバイスで表示する仮想的な表示領域のことで、デバイスの解像度のことではありません。例えば、ViewPort を以下のように指定すると、横幅が 360px
の仮想領域が ViewPort として作られます。
<meta name="viewport" content="width=360">
ブラウザは、画面をレンダリングするとき、デバイスの解像度ではなく、ViewPort に合わせてレンダリングします。ViewPort に表示されている情報は、あたかも 360px
のディスプレイを使っている環境として扱われます。そのため、Javascript で document.documentElement.clientWidth
をコールしても 360
を返します。
ただし、ViewPort のサイズを指定する場合、デバイスの解像度よりも大きいサイズを指定すると、画面が見切れてしまいます。逆に、デバイスの解像度よりも ViewPort のサイズが小さければ余白ができてしまいます。これらを調整するためには、以下のように指定することで端末やブラウザに最適化されたサイズになります。
<meta name="viewport" content="width=device-width">
デバイスのサイズや解像度に応じて適切なサイズの画像を提供する方法としては、picture
要素と srcset
属性を用いた 2 つの方法があります。
ひとつ目の方法は、Media Query を用いた方法です。source
要素の中に media
属性を含めることで、条件に一致する画像を出し分けます。下記の場合、ViewPort の幅が 799px
以下の場合、image1x.png
が表示され、800px
以上であれば image2x.png
が表示されます。
<picture>
<source srcset=image1x.png media="(max-width:799px)">
<source srcset=image2x.png media="(min-width:800px)">
<img src=image2x.png alt="media query">
</picture>
Apple が Retina ディスプレイを発表した当時は、WebKit を独自に拡張した指定方法がありました。その他のブラウザも追従するように拡張されましたが、今ではベンダプレフィックスを定義しない上記の方法が一般的です。
@media (-webkit-min-device-pixel-ratio: 2),
(min--moz-device-pixel-ratio: 2),
(-o-min-device-pixel-ratio: 2/1) {
.class {
background-image: url(image2x.png);
}
}
ふたつ目の方法は、sizes
属性を用いた方法です。この方法は、srcset
属性の中に画像のファイル名と、横幅のピクセルサイズを "w"
の単位を付けて記述します。ピクセルサイズを指定しますが、単位が "px"
ではないことに注意してください。複数の画像を出し分けるリストを作成する場合は ","
で区切って設定します。
sizes
属性には、画面の幅 (ViewPort) などのメディア条件を定義します。メディア条件が満たされた場合、その条件に定義されたスロットの幅が決定し、srcset
属性に設定されたもっとも近い画像が選択されます。例えば、ViewPort が 480px
のブラウザがページを読み込むと、sizes
属性に定義されたメディア条件から (max-width: 480px)
を満たします。そこからスロットの幅が 440px
に決まり、srcset
属性に設定されたもっとも近い 480w.png
が選択されます。
<picture>
<source srcset="320w.png 320w,
480w.png 480w,
800w.png 800w"
sizes="(max-width: 320px) 280px,
(max-width: 480px) 440px,
800px"
<img src=800w.png alt="sizes">
</picture>
画像には様々なデータが含まれており、それらのデータは画像のファイルサイズが大きくなる原因になります。例えば、写真を撮影したときに付与される Exif のメタ情報や、冗長な色深度などです。これらのメタ情報の削除や、減色処理することで見た目はそれほど変化させずにファイルサイズを大幅に減らせることができます。
Exif (Exchangeable image file format) では、以下のメタデータが記録されます。これらのデータサイズは、画像データサイズと比較すると誤差のようなデータ量ですが、個人を特定できる情報が含まれているため削除するべきです。一部のオンラインブログでは、画像のアップロード時に Exif 情報を自動的に削除しているサービスもあります。
Exif 情報を削除する一般的な方法は Windows と Mac で異なります。Windows の場合は、画像ファイルのプロパティを開き、詳細タブの「プロパティや個人情報を削除」機能から削除できます。
Mac の場合は、ImageOptim のソフトを利用して削除します。ImageOptim の使い方については公式サイトをご確認ください。
色深度とは、画像の色をnビットの範囲で表現する単位です。例えば 8 ビットカラーでは、256 色 (28 = 256 色) まで表現可能です。ビット数が大きくなれば表現可能な色は増えますが、グレースケールや単色の場合、24 ビットカラーは冗長です。また、複数の色が使われている画像においても、見た目が気にならないレベルの減色をすることで、画像のファイルサイズを大幅に減らすことができます。
画像を減色するには、オンラインサービスを利用すると簡単にできます。有名なサービスに、TinyPNG があります。このサービスは PNG だけでなく JPEG も対応しています。
WebP 形式は、前述のとおり Google が開発しているオープンソースの静止画用の画像形式です。WebP はその他の画像形式よりも高い圧縮率が期待できますが、すべてのブラウザにおいてサポートされているわけではありません。ブラウザ別の対応状況は、下記のリンクから確認できます。
WebP 形式への変換は、Google から提供されているライブラリを利用します。Windows の場合は、下記のリンクから libwebp
をダウンロードします。
Max/Linux の場合は、パッケージの利用も可能です。
$ brew install webp # mac
$ apt-get install webp # linux
ダウンロードした libwebp
のアーカイブを展開すると、bin
フォルダがあるので、その中の cwebp.exe
を利用して変換を行います。下記は、もっとも単純な方法で WebP ファイルを出力している例です。output.webp
のパラメータに入力ファイル名 (input.png
) と、出力ファイル名 (output.webp
) そしてオプションとして "-o"
を指定しています。パラメータ、およびオプションの詳細は公式サイトをご参照ください。
> cwebp.exe input.png -o output.webp
Saving file input.webp
File: output.png
Dimension: 1000 x 1000
Output: 7836 bytes Y-U-V-All-PSNR 50.24 47.06 47.61 49.04 dB
(0.06 bpp)
block count: intra4: 342 (8.62%)
intra16: 3627 (91.38%)
skipped: 2774 (69.89%)
bytes used: header: 146 (1.9%)
mode-partition: 3333 (42.5%)
Residuals bytes |segment 1|segment 2|segment 3|segment 4| total
macroblocks: | 0%| 5%| 26%| 69%| 3969
quantizer: | 36 | 35 | 32 | 23 |
filter level: | 11 | 7 | 20 | 3 |
上記のコマンドが正しく実行されると output.webp
が出力されます。しかし、上述のとおり WebP のブラウザサポートは限定的であるため、非対応ブラウザには PNG, JPEG, GIF などの別形式で送る必要があります。非対応ブラウザに別形式で画像を送るには picture
要素の中に source
要素を含めることで分岐させます。
<picture>
<source type=image/webp srcset=image.webp>
<img src=image.png width="400" height="400" alt="sample image">
</picture>
上記のように記述することで WebP をサポートしているブラウザは image.webp
をリクエストし、非対応のブラウザは image.png
にフォールバックします。
最高の画像最適化戦略として最初に検討するべきことは、その画像が本当に必要かどうかということです。不必要な画像を省略することは、シンプルで優れたデザインであると同時に最高のパフォーマンスを発揮します。画像は、HTML, CSS, Javascript などに比べ、ダウンロードのリソースにおいて大きなファイルサイズを占めます。そのため、その画像が本当に必要であるかどうか検討することは、最高の画像最適化戦略になります。
もしも画像が本当に必要であれば、どの形式で選択すれば良いのかを検討します。まずは、対象の画像がベクタ形式で表現できるかどうかで SVG 形式が採用候補になります。ラスタ形式の場合は、WebP 形式が採用候補になりますが、WebP が非対応のブラウザ用に PNG または JPEG が候補になります。単純な図形は PNG 形式、そうでなければ JPEG 形式が候補になります。このような正しい画像形式の選択は、大幅なファイルサイズの削減に寄与します。
優れた画像は、多くの文字で説明するよりも速く正しく理解しやすい形で情報を伝えることができます。しかし、だからと言って画像を多用することは必ずしも良い方法とは言えません。対象ページの特性、伝える情報、そして文字とのバランス次第になります。画像を多用する前に CSS によるエフェクト、アニメーション、Web フォントなど、他の技術を使うことで、より効率よく情報を伝えられるかどうかを検討してください。