🐧

スライダープラグインSplideをカスタマイズしてオシャンなサイトのFV再現してみた

2022/08/14に公開

今回再現したサイト

https://laq2.jp/
こちらのサイトのFVの部分です。
(こんなオシャンなサイトいつか実務で作ってみたい...黙r)

完成図
https://www.lets-retire-early.com/Splide/

実装できた仕様

  • スライド切り替えアニメーションの追加
  • スライド切り替えのプログレスバー追加(FV右側にあるゲージみたいなやつ)
  • スライドの枚数と現在のスライドのインデックス番号の取得、表示(今何枚目だよーってやつ)

コードの全体像

Splideの導入方法については、公式サイト(日本語にも対応)を参照ください。

https://ja.splidejs.com/

<section id="image-carousel" class="splide">
      <div class="splide__track">
        <ul class="splide__list">
          <li class="splide__slide">
            <img src="images/cafe-1.jpg" alt="" />
          </li>
          <li class="splide__slide">
            <img src="images/cafe-2.jpg" alt="" />
          </li>
          <li class="splide__slide">
            <img src="images/cafe-3.jpg" alt="" />
          </li>
          <li class="splide__slide">
            <img src="images/cafe-4.jpg" alt="" />
          </li>
          <li class="splide__slide">
            <img src="images/cafe-5.jpg" alt="" />
          </li>
        </ul>
        <div class="pagination">
          <span id="current-slide"></span>
          <span id="progressBarOuter"><span id="progressBar"></span></span>
          <span id="slide-length"></span>
        </div>
      </div>
    </section>
.splide__slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.splide__slide {
  height: 700px;
  overflow: hidden;
  &:before {
    position: absolute;
    content: "";
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    z-index: 2;
    background-color: rgba(grey, 0.3);
  }

  &.is-active {
    img {
      animation-name: fadeInAnime;
      animation-duration: 12s;
      animation-fill-mode: forwards;
      animation-iteration-count: infinite;

      @keyframes fadeInAnime {
        0% {
          transform: scale(1.1);
        }
        100% {
          transform: scale(1);
        }
      }
    }
  }
}

.splide__track {
  position: relative;
}

.pagination {
  position: absolute;
  right: 100px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: flex;
  flex-direction: column;
  color: white;
  align-items: center;
  gap: 10px;

  #progressBarOuter {
    width: 2px;
    height: 100px;
    background-color: white;
    position: relative;

    #progressBar {
      position: absolute;
      content: "";
      width: 100%;
      height: 100%;
      background-color: rgb(60, 60, 60);
      top: 0;
      left: 0;
      animation: linear 6s;
      animation-fill-mode: forwards;
      opacity: 0;
      transform-origin: top;

      @keyframes progressAnime {
        0% {
          transform: scaleY(0);
        }
        100% {
          transform: scaleY(1);
        }
      }
    }
  }
}

const splide = new Splide("#image-carousel", {
  type: "fade",//スライド切り替え時fadeのアニメーション付与
  autoplay: true,//自動再生
  pauseOnHover: false,//スライドにhover時自動再生停止禁止
  pauseOnFocus: false,//スライドfocus時自動再生停止禁止
  rewind: true,//スライドの最後のページに行ったら、最初のページに戻れるようにする
  interval: 6000,//次のスライドに行くまでの速さ
  arrows: false,//デフォルトのページ送りボタンを非表示
  speed: 2000,//ページ送りアニメーションの速さ
  pagination: false,//デフォルトのページネーションを非表示
}).mount();

//アクティブスライドが変わった時に発生(次のスライドが表示されるタイミング)
splide.on("active", function () {
  slideIndex();
  progressBarAnimeStart();
});

//スライダーが動く直前に発生(次のスライドに行く直前に発生)
splide.on("move", function () {
  progressBarAnimeStop();
});


//スライドの枚数と現在のスライドが何枚目かを取得しDOM内に追加
function slideIndex() {
  //splide.indexでスライドのインデックス番号が0から取得される。+1することで現在のスライド枚数が取得できる
  document.getElementById("current-slide").textContent = splide.index + 1;

  //splide.lengthでスライドの枚数が取得可能。
  document.getElementById("slide-length").textContent = splide.length;
}

slideIndex();//ハンドラのイベント実行前にも関数呼び出ししないと1枚目のスライドで番号が表示されない


function progressBarAnimeStart() {
  //[#progressBar]を可視化し、animation-name付与してアニメーション開始
  document.getElementById("progressBar").style.opacity = 1;
  document.getElementById("progressBar").style.animationName = "progressAnime";
}

progressBarAnimeStart();//こちらもハンドライベント実行前に実行するために、ここで呼び出す

function progressBarAnimeStop() {
  //次のスライドにうつる直前でanimation-nameプロパティを空にしてアニメーションを停止させる
  document.getElementById("progressBar").style.animationName = "";
}

ポイント

スライド切り替えアニメーションはCSSで付与

今回は、スライドが徐々にズームアウトするようなアニメーションの再現を行いました。
アニメーション自体は、keyframeで難しくなく実装可能です。
ポイントと思うところとして
Splideは現在のスライドに「is-active」クラスが付与されていく仕様になっていたので
それに対してアニメションを付与した点です。

.splide__slide {
  height: 700px;
  overflow: hidden;
  &:before {
    position: absolute;
    content: "";
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    z-index: 2;
    background-color: rgba(grey, 0.3);
  }

//この部分
  &.is-active {
    img {
      animation-name: fadeInAnime;
      animation-duration: 12s;
      animation-fill-mode: forwards;
      animation-iteration-count: infinite;

      @keyframes fadeInAnime {
        0% {
          transform: scale(1.1);
        }
        100% {
          transform: scale(1);
        }
      }
    }
  }
}

autoplay:trueだけではループしない

タイトルの通りでした。
これだけでループすると思い込んでましたが、「rewind:true」 のオプション指定も必須です。

難航したことと対策

ほんと、スライドの切り替えにかかる時間ワカランです。

プログレスバーのduration設定に難航した

interval: 6000,//次のスライドに行くまでの速さ
speed: 2000,//ページ送りアニメーションの速さ

こうなので、単純に「6000ms」 + 「2000ms」 = 「8000ms」と思うじゃないですか
これは違う。
じゃあ、シンプルに6秒か?
これも違う。

どんどんプログレスバーのアニメーションがずれるずれる。。。

対策

結果、以下で対応しました。

  • ページ読み込み時にまずプログレスバーのアニメーション開始
  • スライドが切り替わる直前にアニメーション一旦停止(animation-nameを空にして強制終了)
  • スライドが切り替わった後に再度アニメーションを開始(animation-nameを付与して開始)

こうすることで、「interval」と同じ秒数を「animation-duration」に設定すれば問題解決!!

最後に

Splide便利ですね。カスタマイズ結構しやすいかもしれない。
今後もオシャンなスライダー探してSplideの可能性広げる旅を続けます。
またいい感じにできたら記事書きたいと思います。
ありがとうございました。

Discussion