ロールオーバーで画像を切り替える時はちらつきに注意する


はじめに

ある要素にカーソルをあわせたときに、画像を切り換える演出をつけることはよくあるかと思います。

その際、初回ロールオーバー時に一瞬画像が消えてから切り替わるように見えることがあります。
これは、切り替え後の画像がロールオーバー発生時に初めてロードされることが原因です。

結構気になるので、これを解消する方法をまとめます。

backgroundでロールオーバーをさせる時

画像をbackgroundを使用して表示している要素をマウスオーバーしたときに、その背景画像を切り替えるのはよくある実装かと思います。例えばボタンの右端の矢印や、「別タブで開く」記号…などなど。

問題のコード

HTML
<div class="hoverimg">
  hoverする要素
</div>
CSS
.hoverimg {
  background: center / contain no-repeat url(/img/sample.png);
}
.hoverimg:hover {
  background-image: url(/img/sample_hover.png);
}

これでは、ページローディング時に読み込まれる画像はsample.pngのみで、ロード後初回hover時に初めてsample_hover.pngが読み込まれるため、一瞬画像が消えたように見えてしまいます。

Chromeであれば、デベロッパーツールでNetworkタブで確認するとこのロードされるタイミングが分かります。

改善したコード

このちらつきを改善するには、事前にロールオーバー後の画像も読み込ませておきます。

JavaScriptを用いる方法もありますが、今回はCSSだけで実装します。

CSS
.hoverimg {
  background: center / contain no-repeat url(/img/sample.png),
              center / 0 no-repeat url(/img/sample_hover.png);
}
.hoverimg:hover {
  background-size: 0, contain;
}

background-sizecontain(または表示サイズ)と0を切り換えることで、表示・非表示を切り替えます。

これにより、初回ロード時に通常時と共にマウスオーバー時の画像もプリロードされるため、ちらつきが発生しなくなります。

imgタグでロールオーバーさせる時

imgタグを使って画像を表示する場合は、また工夫が必要です。

ロールオーバーには、JavaScriptのイベントハンドラであるonmouseoveronmouseoutを使用する方法がありますが、こちらもプリロードされず、onmouseover発生時にロードされます。

  • onmouseover:その要素にカーソルが重なった時
  • onmouseout:その要素からカーソルが外れた時
<img src="/img/sample.png"
     onmouseover="this.src='/img/sample_over.png'"
     onmouseout="this.src='/img/sample.png'">

ということで、JavaScriptでプリロードしておきます。いろいろ方法はあると思いますが一例として。

preload
const _preload = () => {
  // プリロードさせたい画像のパス
  const preloadImagePaths = [
    '/img/sample_over.png',
    ...
  ];

  for (let i = 0; i < preloadImagePaths.length; i++) {
    const img = new Image();
    img.src = preloadImagePaths[i];
  }
};

_preload();

new Image()document.createElement('img')と同じ意味で、プリロード用のダミー要素を生成しています。

まとめ

ロールオーバーで画像を切り換える際に気をつけることをまとめました。

解像度が高い画像などを無闇矢鱈に事前ロードさせないなど(そもそもその設計が悪いかも?)、使いどころには少し注意が必要かと思います。