幅がバラバラな画像を高さを揃えて無限ループさせる方法【CSS】

※本ブログの目的は個人の備忘録であり、コードは参考用として掲載しています。
実際に使用される際は、ご自身の環境で十分に動作確認を行ってください。
コードの利用によって生じたいかなる問題についても責任を負いかねますので、あらかじめご了承ください。

はじめに

画像やロゴを無限に横に流すスライダーは、SwiperなどのJavaScriptライブラリや、CSSだけで作る方法などさまざまあります。
一見どれも「無限ループしているように見える」仕上がりになりますが、画像の横幅がバラバラな場合、次のような不具合が発生しがちです。

例えば、

  • スクロール速度が不安定になる
  • 一瞬止まったように見える
  • つなぎ目でカクつく

これは(特にswiperではそうですが)、多くの実装例が画像の幅を一定と仮定して動きを制御しているためです。

そこで本記事では、横幅が異なる画像でもスムーズに無限ループさせるCSSのテクニックをご紹介します。JavaScriptに頼らず、CSSのみで実現できるので、Swiperでうまくいかなかった方にもおすすめです。

CSSだけで無限ループを実現する

こうした問題はJavaScriptを使わず、CSSだけで実装することで回避できます

CSSだけで画像を無限ループさせる方法は、ネット上にいくつか情報があります。

私は下記のサイトを参考にさせていただきました。仕組みは記事をご確認ください。

【CSSだけ】画像が横に流れ続ける無限ループの作り方 - IE11対応済み | 夢みるゴリラ

ただ、たいていのサイトで紹介されているものは画像幅がきちんと揃っているものしか紹介されていません。上述のサイトでもwidth: calc(100vw / 6)のように画面幅にぴったり合うように画像幅を計算しています。
これは、無限ループしているように見せるために画面幅いっぱいに画像を敷き詰める必要があるからです。画面幅より画像幅の合計が少ない場合、切れ目が見えてしまうということを防ぐためでもあります。

本題ですが、横幅はバラバラで、高さは揃えたい場合もあると思います。

横幅がバラバラな画像をループ表示するには

まず、完成形の無限ループスライダーはこちらです。

画像幅がバラバラの場合、以下のような問題が発生します。

  • 通常の width: auto では flex の影響を受け、意図通りの表示にならない
  • 画像の合計幅がウィンドウ幅より小さいと機能しない

解決のポイントは以下の2点です。

  • width: max-content を使って、画像の実サイズに合わせた幅を確保する
  • min() / max() 関数で可変サイズの制御を行い、画像サイズが小さくなりすぎないようにする

まずはhtmlです。ループさせたい画像一覧の2セット用意します。こちらは参考サイトと仕組みは同じです。(classなどは変更しています。)

<div class="c-loop-slider">
  <div class="c-loop-slider__wrapper">
    <div class="c-loop-slider__items">
      <img src="img1.jpg" alt="">
      <img src="img2.jpg" alt="">
      <img src="img3.jpg" alt="">
    </div>
    <!-- 複製 -->
    <div class="c-loop-slider__items">
      <img src="img1.jpg" alt="">
      <img src="img2.jpg" alt="">
      <img src="img3.jpg" alt="">
    </div>
  </div>
</div>

肝心のCSSはこちらです。

// アニメーション設定(左に流れる)
@keyframes infinity-scroll {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-100%);
  }
}
.c-loop-slider {
  overflow: hidden;
}
.c-loop-slider__wrapper {
  display: flex;
  width: max-content; // スライダー全体の横幅を内容に合わせて拡張
}
.c-loop-slider__items {
  display: flex;
  animation: infinity-scroll 30s linear infinite both;
}
.c-loop-slider__items img {
  margin-right: 20px; // 複製の間に余白をつける
  width: auto;
  height: 240px;

  // 画面幅によって高さ調整
  @media screen and (min-width: 900px) {
    height: min(340 / 900 * 100vw, 340px);
  }

  @media screen and (min-width: 1200px) {
    height: max(340 / 1200 * 100vw, 340px);
  }
}

width: max-content とは

画像を横並びにする際、一般的には flex を使用します。画像の幅がすべて同じであれば特に問題はありませんが、画像ごとに幅が異なる(いわゆる「成り行き」)場合、width: auto を指定してもうまく表示されないことがあります。

これは、display: flex の仕様により、子要素が自動的に親の幅や他の子要素とのバランスに影響を受け、意図しないサイズ調整が行われてしまうためです。特に画像をインラインで表示したい場合や、余白なしで自然なサイズのまま並べたいときに問題が生じます。

そこで有効なのが width: max-content の指定です。max-content は、中の要素が収まる最大幅を保ちたいときに使います。

これにより、flex の親要素が横幅を無理に縮めたりせず、画像本来のサイズで自然に並べることができます。

min() / max() 関数を活用してサイズ調整

画像の横幅を成り行き(width: auto)で指定している場合、画像全体の合計幅がウィンドウサイズ(表示領域)よりも小さくなると、ループアニメーションに切れ目が生じ、ループアニメーションがうまくいきません。

そのため、画像の「高さ」を調整して、結果的に横幅がある程度確保されるようにすることが重要です。

たとえば、次のような指定を使うことでウィンドウ幅に応じて画像の高さが自動で変化し、「小さすぎず」「大きすぎない」サイズに抑えることができます。

.c-loop-slider__items img {
  margin-right: 20px; // 複製の間に余白をつける
  width: auto;
  height: 240px;

  // 画面幅によって高さ調整
  @media screen and (min-width: 900px) {
    height: min(340 / 900 * 100vw, 340px);
  }

  @media screen and (min-width: 1200px) {
    height: max(340 / 1200 * 100vw, 340px);
  }
}

このように min()max() を使い分けることで、ウィンドウサイズに応じた自然なレスポンシブ調整が可能になります。

なお、今回のテクニックは下記のブログ記事を参考にさせていただきました。画像サイズの調整やレスポンシブな配置に興味のある方は、ぜひチェックしてみてください。

絶対配置をレスポンシブしたいとき。 - とりとめないメモ。

gapの代わりに margin を使う理由

flexgap を使うと、要素の複製部分(ループの切れ目)で予期せぬ隙間ができる場合があります。
そのため、確実に同じ間隔で並べたい場合は margin で対応するのがよさそうです。

まとめ

無限ループするスライダーの実装はさまざまありますが、画像の幅がバラバラなケースではSwiperなどのライブラリではうまくいかないことがあります。

その場合は、今回ご紹介したようにCSSだけで完結する方法が有効です。軽量で実装もシンプルかつ滑らかな動きを実現できるので、流れ続ける無限ループスライダーで困っている方はぜひ試してみてください。

参考

【CSSだけ】画像が横に流れ続ける無限ループの作り方 - IE11対応済み | 夢みるゴリラ
絶対配置をレスポンシブしたいとき。 - とりとめないメモ。