PCでは問題ないのにスマホで画像表示が止まる―重い画像が与える影響とAstroでの対策

PCでは問題なく読めたページが、スマホでは画像付近で止まったように見えた経験から、重い画像がモバイル表示へ与える影響を確認します。ゆまラボでAstroの画像最適化、srcset、WebP、遅延読み込みを適用した実測も振り返ります。

執筆:ゆまラボ運営者

公開:
PCでは問題ないのにスマホで画像表示が止まる―重い画像が与える影響とAstroでの対策

PCでは普通に表示できているのに、スマホで同じページを開くと画像の途中で止まったように見える。

先日、実際にこの状態になった。同じページなのにPCでは普通に見えてスマホだけ止まったように見えるのは、かなり気になった。

PCでは最後まで問題なく読める。スマホでは記事を下へ進んでいく途中、画像が出てくるあたりから先へ進みにくくなった。

その時点ではNetworkログを保存していないため、「画像の重さだけが原因」とまでは断定できない。

ただ、当時のゆまラボには明確に改善できる状態が残っていた。

記事本文のスクリーンショットは大きなPNGのままpublic/images/から配信しており、確認したimgにはsrcsetsizesloadingwidthheightが付いていなかった。

その後、第27回 画像表示を見直す―Astroの画像最適化と遅延読み込みでページを軽くするで本文画像の配信方法を変更した。

結果として、実際にスマホでの表示は軽くなった。

今回は実装手順そのものではなく、なぜPCで問題が見えなくてもスマホでは画像の重さが効きやすいのか、ゆまラボで何を変えたのかを確認する。

画面で小さく表示しても転送する画像は小さくならない#

修正前のゆまラボでは、本文画像を次のような通常のimgとして出していた。

<img
  src="/images/blog/.../screenshot.png"
  alt="..."
>

画像はCSSで本文幅に収まる。

PCでもスマホでも、見た目だけならレスポンシブに表示できる。

ただし、表示幅をCSSで小さくすることと、ブラウザが取得するファイルを小さくすることは別になる。

例えば元画像が1,500px幅でも、スマホ画面では300px台で表示できる。

その状態で候補画像が1つしかなければ、ブラウザは大きな元画像を取得してから画面幅へ縮小表示することになる。

MDNのResponsive Imagesでも、狭い画面でデスクトップ向けの大きな画像をそのまま取得すると帯域を無駄にしやすく、srcsetsizesによって端末に合う候補をブラウザへ選ばせる方法が案内されている。

修正前の実画面をDevToolsで確認すると、本文画像は通常の画像URLを直接参照する形。

画像最適化前の記事本文とDevToolsでimg要素を確認した実画面

スマホだから画像ファイルが自動的に軽くなるわけではない。

ここは表示サイズだけを見ていると気付きにくい。

スマホでは画像の待ち時間が目立ちやすい#

重い画像があるからページのHTML解析そのものが必ず止まる、という話ではない。

ブラウザはHTMLを読みながら画像の取得も進める。

ただ、本文に大きな画像が何枚もあり、必要以上に大きなファイルを転送していれば、モバイル回線では画像が表示されるまでの待ち時間が長くなりやすい。

PCで高速な回線につないでいると問題に見えなくても、スマホでは条件が変わる。

MDNも、モバイル端末は大きな画像を必要としないケースがあり、回線速度や通信量の面でも大きな画像をそのまま取得するのは無駄になり得るとしている。

もう一つ、修正前の本文画像にはwidthheightもなかった。

画像の寸法をHTMLで先に示しておけば、ブラウザは画像取得前から表示領域を確保できる。

寸法がなければ、画像を読み込んだ後に周辺要素の位置が変わることがある。ページが重い状態では、このような動きも「読み込みが終わらない」「途中で引っ掛かる」という感覚につながりやすい。

今回の実体験では、端末側のメモリ使用量やデコード時間までは測っていない。

そこまでを原因として書くことはしない。

確認できているのは、大きなPNGをそのまま配信していたことと、その配信方法を変更した後にスマホ表示が軽くなったこと。

ゆまラボでは本文画像をAstroの画像処理へ移した#

対策では、本文画像をpublic/images/からそのまま返す方式をやめた。

現在は画像をsrc/assets配下へ置き、Markdownから相対パスで参照する。

src/
├─ content/
│  └─ blog/
│     └─ ...
└─ assets/
   └─ images/
      └─ blog/
         └─ ...

Astroの公式ドキュメントでは、可能な限りローカル画像をsrc/へ置くことが推奨されている。

src/内の画像はAstroが変換・最適化できる。

一方、public/内の画像はビルド時にそのままコピーされ、画像処理の対象にならない。

ゆまラボでは既存Markdownを1記事ずつ手作業で変更するのではなく、本文画像用のrehype処理からAstroの画像処理へ渡すようにした。

生成された候補幅は代表ページで、

320w
480w
640w
720w

の4段階。

sizesは以下。

sizes="(min-width: 720px) 720px, 100vw"

ブラウザ側が表示条件に合わせてsrcsetから画像を選べる。

WebPも生成する。

ビルドでは大量の最適化画像が生成される状態に変わった。

Astroのビルドで本文用の最適化画像が生成されている実画面

さらに、画面下の本文画像にはloading="lazy"が付く。

loading="lazy"

修正時の確認では、下方の画像が最初から全部取得されず、スクロールするまで読み込みが保留されることも確認した。

ページを開いた直後に必要のない画像まで一斉に取りに行かない。

画像が多い技術記事では、この差も大きい。

代表ページの対象画像は122,901 bytesから26,698 bytesになった#

サイト全体では、実ファイル278件、本文内281参照を対象にした。

元画像の合計は、

44,399,561 bytes

Astroで生成した本文画像候補の合計は、

11,315,090 bytes

削減率は74.52%。

ただし、これは「サイトが74.52%速くなった」という意味ではない。

生成候補画像の容量比較であり、HTML、CSS、JavaScript、フォント、通信待ち時間などは含まない。

実際のページでは、第25回の404記事を代表ページとして確認した。

修正前に対象としていた画像は合計、

122,901 bytes

Luna版を本番採用した後、PCで実際に選択された画像は合計、

26,698 bytes

78.28%減っている。

この数字もページ全体の速度改善率ではなく、対象画像の転送量。

それでも、同じ見た目の画像を出すために取得するバイト数が大きく減ったことは確認できる。

スマホ相当では、

390 × 844

で表示を確認した。

本文幅343pxに対して画像幅も343px。横スクロールは発生せず、縦横比やスクリーンショット内の文字も確認できた。

対策後、実際のスマホ表示も以前より早く感じられる状態になった。

ここは容量の計測値とは分けて、実使用時の体感として扱う。

画像を軽くするときは圧縮だけを見ない#

画像対策というと、PNGをWebPへ変換してファイルサイズを減らす話だけになりやすい。

今回のゆまラボでは、それだけではない。

大きなPNGをそのまま配信

src/assetsへ配置

Astroで複数幅のWebPを生成

srcset / sizesで端末に合う候補を選択

width / heightで表示領域を確保

画面外の画像はlazy loading

という流れへ変更した。

スマホに効くのは、「圧縮率が高い画像を作った」ことだけではなく、スマホに必要以上に大きな画像を送らないことと、まだ見えていない画像を急いで取得しないことになる。

今後ゆまラボへ追加する本文画像もsrc/assetsへ置く。

この単発記事の画像も、

src/assets/images/blog/web-development/design/mobile-heavy-images/

へ配置し、Markdownから相対パスで参照している。

public/images/へ戻すと、その画像だけ現在の本文画像用の処理から外れるため注意が必要になる。

PCで正常に見えているだけでは、画像配信が軽いかどうかまでは分からない。

スマホで表示が遅い場合は、画像の見た目だけでなく、Networkで実際に何bytes取得しているか、srcsetがあるか、画面外画像が最初から読み込まれていないかを見る。

今回のゆまラボでは、ここを変更した後に転送量が減り、スマホでも表示が軽くなった。

参考#