【Blogger】軽量・高速なCSSカルーセルの導入方法と解説

2021/10/31
7
thumbnail

ときどき、Webサイトでスライドショーのようなものを見かけると思います。あれはカルーセルと呼ばれていて、「回転木馬」という意味があります。メリーゴーランドみたいにぐるぐる回っている様子から名づけられたみたいですね。

今回、そんなカルーセルをBloggerへ簡単に導入できるようにしました。コードと導入方法に加えて、動作原理についても詳しく解説。仕組みがわかれば、Blogger以外にも様々な場所で応用できます。

はじめに

今回のカルーセルはアニメーション処理にJavaScriptを使用しておらず、基本的にCSSのみで動作します。そのため、非常に軽いのが特徴。

実はこのCSSカルーセル、私のアイデアではなく下記の「よっし~ずウェブサービス」さんのものを全面的に参考にさせて頂いたものです。

よっし~ずウェブサービス【Yws】

Yossy's web serviceという屋号でWordPressの設置代行や画像作成など格安で承ります。テーマの販売もしています。ご相談はお気軽に٩(ˊᗜˋ*)و

カルーセルってJavaScript使わないと不可能じゃないかと思っていたのですが、コードを見て目から鱗という感じでした。

導入方法

バックアップ

まずは不測の事態に備えてバックアップから。

管理画面から「テーマ」→「バックアップ」を選択し、ダウンロードしたファイルを保存しておきましょう。

backup-img

コード設置

以下がCSSカルーセルのソースコードです。

<!-- Carousel -->
<div class='carousel'></div>
<script type='text/javascript'>
  const carSelector = '.carousel';
  const carNum = 10;
  const showNum = 5;

  //<![CDATA[
  insertCar(carSelector, carNum);

  function insertCar(carSelector, carNum) {
    let feedUrl = '/feeds/posts/summary?alt=json&max-results=' + carNum;

    fetch(feedUrl)
      .then((response) => {
        return response.json();
      })
      .then((json) => {
        let entry, title, url, img;
        let titles = [];
        let urls = [];
        let imgs = [];
        let html = '';
        if (json.feed.entry.length < carNum) {
          return
        }
        for (let i = 0; i < json.feed.entry.length; i++) {
          entry = json.feed.entry[i];
          title = entry.title.$t;
          for (let j = 0; j < entry.link.length; j++) {
            if (entry.link[j].rel == 'alternate') {
              url = entry.link[j].href;
              break;
            }
          }
          if ("media$thumbnail" in entry) {
            img = entry.media$thumbnail.url;
            if (img.match(/default.jpg/)) {
              img = img.replace('default.jpg', 'mqdefault.jpg');
            } else {
              img = img.replace(/([/=])s[0-9]+.*-c/, '$1w240-h144-n');
            }
          } else {
            img = "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJGufis0idCDqb29dBvyQCx5M5dHTE4bpc52w6WGtcdKpsQImJUUvWSFYn5k_IDsn1TjOhyfzw51TgvvabIrVAYjxasRmPjGTPdSKudZTYD6JA5DLW2rm-9fkahTfV4Cmn-GjsDaiCDnJ5/w240/noimg.jpg";
          }
          html += '<div class="carousel-' + i + '"><a href="' + url + '"><figure class="car-img"><img loading="lazy" alt="" width="240" height="144" src="' + img + '"></figure><span class="car-title">' + title + '</span></a></div>';
          titles.push(title);
          urls.push(url);
          imgs.push(img);
        }
        for (let k = 0; k < showNum; k++) {
          html += '<div class="carousel-' + (carNum + k) + '"><a href="' + urls[k] + '"><figure class="car-img"><img loading="lazy" alt="" width="240" height="144" src="' + imgs[k] + '"></figure><span class="car-title">' + titles[k] + '</span></a></div>';
        }
        const carousel = document.querySelector(carSelector);
        if (carousel != null) {
          carousel.innerHTML = html;
          carousel.outerHTML = '<div class="footer-carousel"><div class="carouselWrap"><div class="carouselBlock">' + carousel.outerHTML + '</div></div></div>';
        }
      })
      .catch((error) => {
        console.log(error);
      });
  }
  //]]>
</script>
<style>
  .footer-carousel {
    background: grey;
    padding: 1em 0;
  }

  .carouselWrap {
    max-width: 1120px;
    margin: 0 auto;
    overflow: hidden;
  }

  .carouselBlock {
    position: relative;
    width: 20%;
    padding-top: 18%;
  }

  .carousel {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    animation: carousel-anim 30s infinite;
  }

  .carousel a, .carousel a:hover {
    text-decoration: none;
  }

  .car-img {
    position: relative;
    margin: 0;
  }

  .car-img:before {
    display: block;
    content: "";
    padding-top: 60%;
  }

  .car-img source, .car-img img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .car-title {
    display: block;
    height: 42px;
    line-height: 21px;
    font-size: 15px;
    color: #fff;
    margin: 4% 0 0;
    overflow: hidden;
  }

  [class*="carousel-"] {
    position: absolute;
    width: 100%;
    height: auto;
    top: 0;
    padding: 10% 5% 0;
    box-sizing: border-box;
  }

  .carousel-0 {left: 0%}
  .carousel-1 {left: 100%}
  .carousel-2 {left: 200%}
  .carousel-3 {left: 300%}
  .carousel-4 {left: 400%}
  .carousel-5 {left: 500%}
  .carousel-6 {left: 600%}
  .carousel-7 {left: 700%}
  .carousel-8 {left: 800%}
  .carousel-9 {left: 900%}
  .carousel-10 {left: 1000%}
  .carousel-11 {left: 1100%}
  .carousel-12 {left: 1200%}
  .carousel-13 {left: 1300%}
  .carousel-14 {left: 1400%}

  @keyframes carousel-anim {
    0% {left: 0%}
    8% {left: 0%}
    9% {left: -100%}
    18% {left: -100%}
    19% {left: -200%}
    28% {left: -200%}
    29% {left: -300%}
    38% {left: -300%}
    39% {left: -400%}
    48% {left: -400%}
    49% {left: -500%}
    58% {left: -500%}
    59% {left: -600%}
    68% {left: -600%}
    69% {left: -700%}
    78% {left: -700%}
    79% {left: -800%}
    88% {left: -800%}
    89% {left: -900%}
    98% {left: -900%}
    99% {left: -1000%}
    99.5% {left: -1000%}
    99.501% {left: 0%}
  }

  @media only screen and (min-width: 541px) and (max-width: 960px) {
    .carouselWrap {
      width: 91%;
    }
    .carouselBlock {
      width: 33.33%;
      padding-top: 32%;
    }
  }

  @media screen and (max-width: 540px) {
    .carouselWrap {
      width: 94%;
    }
    .carouselBlock {
      width: 50%;
      padding-top: 50%;
    }
    .car-title {
      font-size: 14px;
    }
  }
</style>

★圧縮版はこちら↓

コードを確認

上記コードをコピーし、お好きな場所に設置してください。

おすすめはページ下部(フッターの前あたり)です。フィードを利用している関係で表示が若干(0.5秒くらい?)遅れるため、下部に設置したほうが違和感が少ないかなぁと思います。

QooQの場合

QooQ(ver1.30)をお使いの場合、<div id='footer'>の上あたりがちょうど良いと思います。下の画像の位置を参考に設置してみてください。

carousel1

仕様説明

カルーセルは、デフォルトで最新10記事をスライドショー風に表示します。実際の動作はこのブログ下部にあるカルーセルで確認してみてください。

レスポンシブ表示に対応しており、961px以上は5記事分、541~960pxは3記事分、540px以下は2記事分が一度に表示されます。背景は他と区別するためグレーにしていますが、好みに合わせて変更可能です。

また、画像URL取得時の正規表現を工夫しているため、画像URLが1.bp.blogspot.comblogger.googleusercontent.comのどちらの場合でもちゃんと動作します。

コード解説

今回のコードで行っていることは主に2つ。JavaScriptによるカルーセル本体の作成と、CSSによるアニメーションです。

まず、JavaScriptではフィードから最新10記事の情報を取得し、カルーセルを生成します。コードor本体を見て注目して頂きたいのが、中身の要素の数です。今回表示するのは全部で10個ですが、.carousel内部にある要素は全部で15個。

carousel2

「??」って感じですよね。実は最後の5個、表示を合わせるためのダミーなんです。

ここでCSSについて見てみると、表示領域.carouselWrap内にはデフォルトで要素が5個表示されるようになっています。.carouselには要素が15個入っていて、アニメーションの際に要素1個分ずつ左側へグイッと引っ張られていくイメージです。

illust1

このまま引っ張り続けたとき、カルーセルが1周するタイミングがやってきます。その直前のイメージ画像がこちら。

illust2

オレンジ色が本体の10番目の要素で、その後に続く1~4はダミー要素(11~14)です。循環して回り続けているように見せるため、あえてダミー要素を配置していたというわけですね。

では、完全に1周したらどうするのか?

完全に1周したとき、ダミー要素の1~5(11~15)が表示領域に表示され、本体の1~5が表示されているのと同じ状態になります。ここで注目して頂きたいのが次のコードです。

@keyframes carousel-anim {
  ...
  99.5% {left: -1000%}
  99.501% {left: 0%}
}

なにやら怪しげな操作をしていますね。よく見てみると、left: -1000%で要素10個分左にずらしたところから、left: 0%で一気に最初まで戻しています。1周したタイミングでカルーセル本体を超高速で巻き戻すことで、あたかも循環して回り続けているように見える…というわけですね。実にトリッキーな方法です。

この仕組みさえわかれば、手動で作成したカルーセルに対しても応用できます。表現の幅が広がると思うので、ぜひトライしてみてください。

7件のコメント
匿名で失礼いたします。
こちらのカルーセルをEmporioに設置してみました。
ほぼ問題なく動作していると思いましたが、10記事目のサムネイルだけが取得できません。
該当する記事の画像を変更したり、遅延読み込みを外すなど試しましたがダメでした。
記事タイトルなどは取得できています。
考えられる原因などあれば教えて頂きたいです。
>匿名さん
サムネイルだけが取得できていないとなると、10記事目の画像のURLパラメータ変換処理がうまくいっておらず、エラーで表示されていないのかもしれません。
直接確認できないので確かなことは言えませんが、上記の可能性が一番高いかと思われます。
Fumaさん、ご返信ありがとうございます。
いくつかのテストサイトで確認しましたが、Emporioでだけで発現するみたいです。
いくつか共通の設定やカスタマイズを施しているので、その辺も影響しているのかもしれません。
Emporioでの使用は一旦見送ることと致しました。
別テーマを使うときに利用させていただきます。
その後「Emporio」にて再度試してみました。ガジェット(レイアウト)で設置したら全てのサムネイルが表示されました?理由は不明ですが、同じような問題で設置を見送る方がいるかもしれないので報告させて頂きました。ありがとうございました。
>匿名さん
ご報告ありがとうございます。
無事に表示されたようで良かったです。
はじめまして!
スライドショー機能がこんな簡単に実装できるのですね。とても参考になりました。
自動目次と併せて導入させていただきました。ありがとうございます。
>AI image journeyさん
はじめまして。コメントありがとうございます。
記事がお役に立ったようで何よりです。