廣瀬製紙株式会社

Employees' Blog

アーチ型のスライダーをWebサイトに実装 その1
【Swiper.jsでチャレンジしてみた編】

公開日:2025.02.25 更新日:2025.02.28
猫の画像を使った円型のスライダー

アーチ型・円型のスライダーって意外とない?

こんにちは!廣瀬製紙株式会社 稼働率向上PJチームのA.Mです。

前職ではWeb系のエンジニアをしており、Webサイトにスライダーを実装するということもよく対応していました。
その際にアーチ型のスライダーを作りたいという要望もデザイナーさんから出たことがありました(結局私は対応しなかったのですが)。

その後色々とWebサイトを見ていても、意外とアーチ型のスライダーを見る機会がないなぁと気づきました。
ですが、需要はありそうですよね?

そんなこんなで、今回はSwiper.jsを使ったアーチ型のスライダーの実装にチャレンジしてみました。

まずはサンプル

上記のネコちゃんの画像(我が家の飼い猫です!)をスライドさせてみてください。

全体が円環状に回り、かつそれぞれのスライドが円の中心を向くような形で回転するかと思います。
ただしSwiperを使って実装するにあたって、いくつか問題点がありました。

うまく実装すれば解決させられるのかもしれませんが、これだけ作るだけでも正直なところそこそこ苦戦してしまいパッと解決できそうなありません。。。

実際のところ、Swiperを使いながら無理くり作るよりは、Canvasなどで自分で最初から実装してしまったほうがいいような気もします。
それは今後の課題としてまたチャレンジしてブログにアップしたいと思いつつ、まずは実装上の課題を読者のみなさまに共有させていただこうと思います。

実際のコード

<div class="swiper">
 <div class="swiper">
  <div class="swiper-wrapper">
    <div class="swiper-slide">
      <img src="https://www.hirose-paper-mfg.co.jp/wp2021/wp-content/uploads/2025/02/cat0-300x300.jpg" alt="猫の画像1">
    </div>
    <div class="swiper-slide">
      <img src="https://www.hirose-paper-mfg.co.jp/wp2021/wp-content/uploads/2025/02/cat5-300x300.jpg" alt="猫の画像2">
    </div>
    <div class="swiper-slide">
      <img src="https://www.hirose-paper-mfg.co.jp/wp2021/wp-content/uploads/2025/02/cat4-300x300.jpg" alt="猫の画像3">
    </div>
    <div class="swiper-slide">
      <img src="https://www.hirose-paper-mfg.co.jp/wp2021/wp-content/uploads/2025/02/cat3-300x300.jpg" alt="猫の画像4">
    </div>
    <div class="swiper-slide">
      <img src="https://www.hirose-paper-mfg.co.jp/wp2021/wp-content/uploads/2025/02/cat2-300x300.jpg" alt="猫の画像5">
    </div>
    <div class="swiper-slide">
      <img src="https://www.hirose-paper-mfg.co.jp/wp2021/wp-content/uploads/2025/02/cat1-300x300.jpg" alt="猫の画像6">
    </div>
    <div class="swiper-slide">
      <img src="https://www.hirose-paper-mfg.co.jp/wp2021/wp-content/uploads/2025/02/cat7-300x300.jpg" alt="猫の画像7">
    </div>
  </div>
</div>

<style>
  body {
    margin: 0;
    padding: 0;
  }

  .swiper {
    width: 100%;
    max-width: 800px;
    height: 300px;
    background-color: #e7eeff;
  }

  .swiper-slide {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    /* 弧の中心に回転させるための原点を下側に */
    transform-origin: center bottom;
  }

  .swiper-slide>img {
    width: 100%;
    object-fit: cover;
    aspect-ratio: 1/1;
    border-radius: 50%;
  }
</style>

<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
<script>
  const swiperWrapper = document.querySelector(".swiper-wrapper");
  const slides = document.querySelectorAll(".swiper-slide");
  const slidesCount = slides.length * 2 || 5;

  //スライドを複製して2倍にする
  slides.forEach((slide) => {
    const clonedSlide = slide.cloneNode(true); // true を指定すると子要素も複製
    swiperWrapper.appendChild(clonedSlide);
  });

  const swiper = new Swiper('.swiper', {
    slidesPerView: 5,
    centeredSlides: true,
    spaceBetween: 0,
    watchSlidesProgress: true,
    loop: true, // 必要に応じてループ
    freeMode: {
      enabled: true,
      momentum: false,
      sticky: false,
    },

    on: {
      setTranslate() {
        const radius = 100; // 弧の半径(px)
        const maxAngle = 360 / slidesCount;

        this.slides.forEach((slide) => {
          // watchSlidesProgress:true により、slide.progress が -1〜0〜1 の範囲を返す
          const progress = slide.progress;

          // スライドがどのくらい中心からズレているかを角度に反映
          const angle = progress * maxAngle;
          const rad = (angle * Math.PI) / 180;

          // 円弧上の X, Y 位置を計算
          const translateX = radius * Math.sin(rad);
          const translateY = radius * (1 - Math.cos(rad));

          // 円の中心を向かせるために角度の反対方向で回転
          const rotate = -angle;

          // スタイルを適用 (Vanilla JS)
          slide.style.transform = `translate3d(${translateX}px, ${translateY}px, 0) rotate(${rotate}deg)`;
        });
      },

      setTransition(duration) {
        // スワイプ中やスライド移動時のアニメーション速度を反映
        this.slides.forEach((slide) => {
          slide.style.transitionDuration = `${duration}ms`;
        });
      },
    },
  });
</script>

Swiperで実装するうえでの問題点

freeModeの設定をいじらないと動きがカクつく

下記を動かしてみてください。

全体の動きとしては最初の実装よりもむしろ滑らかな感じがします。それはスライド終わりにマウス(もしくは指)を離した後で、惰性で回転を続けるようになっているからです。

それがSwiperのデフォルトの動作なのですが、逆にマウス(もしくは指)を離した後で少しカクつきが出てしまいます。

なので最初の実装では、freeModeというパラメーターでmomentumとstickyをfalseにすることで、惰性で動かないようにしてしまいました。
ですが動きが唐突にピタッと止まってしまうのでやはりあまり滑らかさは感じませんね。。。

freeMode: {
  enabled: true, //フリーモードをON
    momentum: false, //惰性の動きをOFF
    sticky: false, //スライドがキリのいい場所でピタッと止まるのをOFF
},

autoplayでもカクつく

次に下記ですが、autoplayをONにした場合です。
(自動で動かない場合は、手動で最初に少し動かしてみてください)

やはりカクつきますね・・・。

動きがカクついてしまう原因

これらのカクつきが起きてしまう原因は、マウスで動かした際の円環状の動きをJavaScriptでアニメーションしているのに対し、
マウスを離した際の動きとautoplayの動きが完全にCSSのみでアニメーションしていることによるものです。

JavaScriptでの実装では、マウスである座標からある座標へと動かしているあいだ、つねに座標位置を再計算していますが、CSSアニメーションではある座標からある座標へと直線状にアニメーションしてしまうようになっているからこのようにカクついて見えてしまうんですね。

解決するためには「CSSのアニメーションを完全に切ってしまい、JSで惰性の動きやautoplayも再現する」「CSSアニメーションが曲線になるように円の動きとぴったり合わせる」のどちらかになりそうですが、どちらにせよかなり大変そうです

どうせそんな大変な実装になるのであれば、Swiperを使ってちょっと無理やりに実装せずに、最初からCanvasなどできれいな実装をしたほうがいいんじゃないかなぁというのが今のところの所感です。

今後も取り組みたい

アーチ型や、完全な円型のスライダーは取り組んでいて面白いので、今後も継続してチャレンジしてブログ記事をアップしていきたいと思います。

コピペで貼り付けたらそれでWebサイトに導入できるように、いろいろ検証してみたいと思いますので、ぜひまた社員ブログを確認しに来てみてくださいね!